The custom-device interface

The custom-device interface permits access to a device of a specific class and model without requiring the creation of an interface for that device alone. It’s intended to be used with Ubuntu Core and its scope and specification are defined as part of the gadget snap for the deployed Ubuntu Core image.

The slot-side of the interface is used to derive which udev rules are provided to the plug-side of the connection:

slots:
  dual-sd:
    interface: custom-device
    custom-device: my-dual-sd-device
    devices: 
      - /dev/DualSD

To prevent connection to arbitrary custom-device slots, the plug and slot must share the same custom-device attributes, including the name of the plug or slot:

plugs:
  dual-sd:
    interface: custom-device
    custom-device: my-dual-sd-device
apps:
  app:
    plugs: [dual-sd]

When the slot and plug are connected, the udev rule is automatically generated and tagged for the plug side, such as:

KERNEL=="DualSD",

Note that here, the KERNEL specification is the devices or read-devices attribute, stripped of the /dev/ prefix.

Requires snapd version 2.55+.

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


Developer details

Auto-connect: no
Super-privileged: no

Attributes:

  • custom-device (plug, slot): label for the custom device.
    Needs to be identical across the plug and slot connections.
  • devices (slot): path to device node.
    Example: devices: [- /dev/input/event[0-9], - /dev/input/mice]
  • files (plug):
    • read (plug): list of files and/or directories for read-only access by the device.
      Example: read: [ /dev/input/by-id/* ]
    • write (plug): list of files and/or directories for read/write access by the device.
      Example: write: [ /etc/file-write, /etc/dir-write ]
  • udev-tagging (optional): used to tailor the generated udev rules. Can be one of the following:
    • kernel: (mandatory): maps to the string used as the udev KERNEL== filter rule.
    • subsystem: corresponds to the SUBSYSTEM== filters in a udev rule.
    • environment: a map of expected environment variables for the udev rule to match with ENV{...}=="..."
    • attributes: a map of attributes used with ATTR{...}=="..."

Code examples

A truncated example showing how the subsystem and attributes can be used:

   udev-tagging:
     - subsystem: usb
       attributes:
         idVendor: "0x03f0" # HP
     - subsystem: usb
       attributes:
         idVendor: "0x03fc" # ECS

An example slot declaration showing the how the kernel environment settings can be used with a custom joystick interface:

slots:
 hwdev:
  interface: custom-device
  custom-device: custom-joystick
  devices:
      - /dev/input/js{[0-9],[12][0-9],3[01]}
      - /dev/input/event[0-9]*
  files:
      read:
        - /run/udev/data/c13:{6[5-9],[7-9][0-9],[1-9][0-9][0-9]*}
        - /run/udev/data/c13:{[0-9],[12][0-9],3[01]}
        - /sys/devices/**/input[0-9]*/capabilities/*
  udev-tagging:
      - kernel: js[0-9]*
      - kernel: event[0-9]*
        subsystem: input
        environment:
          ID_INPUT_JOYSTICK: "1"
      - kernel: full
        subsystem: mem

The above example will generate the following udev tags:

spec.TagDevice(`KERNEL=="js[0-9]*"`) 
spec.TagDevice(`SUBSYSTEM="input", KERNEL=="event[0-9]*" ENV{ID_INPUT_JOYSTICK}="1"`) 
spec.TagDevice(`SUBSYSTEM="mem", KERNEL=="full"`) 

The test code can be found in the snapd repository: https://github.com/snapcore/snapd/blob/master/interfaces/builtin/custom_device_test.go

The source code for the interface is in the snapd repository: https://github.com/snapcore/snapd/blob/master/interfaces/builtin/custom_device.go

Hi,

Thanks for the documentation on custom-device plug.

Had query on udev-tagging option.

In custom-joystick example, js and event nodes defined as below

 devices:
      - /dev/input/js{[0-9],[12][0-9],3[01]}
      - /dev/input/event[0-9]*
  udev-tagging:
      - kernel: js[0-9]*
      - kernel: event[0-9]*
        subsystem: input

I too tried similar slot declaration. But getting snap warning and custom-device plug not working.

That is because of below check in custom_device.go (https://github.com/snapcore/snapd/blob/master/interfaces/builtin/custom_device.go#L160) .

// furthermore, the kernel name must match the name of one of
// the given devices
if !strutil.ListContains(devices, "/dev/"+deviceName) {
		err = fmt.Errorf(`%q does not match a specified device`, deviceName)
}

To resolve error, need to define “kernel” field as below.

  udev-tagging:
      - kernel: input/js[0-9]*
      - kernel: input/event[0-9]*
        subsystem: input

With this, udev tags generating like

spec.TagDevice(`KERNEL=="input/js[0-9]*"`) 
spec.TagDevice(`SUBSYSTEM="input", KERNEL=="input/event[0-9]*")

But with this udev-tag, custom-device plug not working for me as expected and my snap fails to access dev node with EPERM error.