github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/gpio.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin
    21  
    22  import (
    23  	"fmt"
    24  
    25  	"github.com/snapcore/snapd/interfaces"
    26  	"github.com/snapcore/snapd/interfaces/apparmor"
    27  	"github.com/snapcore/snapd/interfaces/systemd"
    28  	"github.com/snapcore/snapd/snap"
    29  )
    30  
    31  const gpioSummary = `allows access to specifc GPIO pin`
    32  
    33  const gpioBaseDeclarationSlots = `
    34    gpio:
    35      allow-installation:
    36        slot-snap-type:
    37          - core
    38          - gadget
    39      deny-auto-connection: true
    40  `
    41  
    42  var gpioSysfsGpioBase = "/sys/class/gpio/gpio"
    43  
    44  // gpioInterface type
    45  type gpioInterface struct{}
    46  
    47  // String returns the same value as Name().
    48  func (iface *gpioInterface) String() string {
    49  	return iface.Name()
    50  }
    51  
    52  // Name of the gpioInterface
    53  func (iface *gpioInterface) Name() string {
    54  	return "gpio"
    55  }
    56  
    57  func (iface *gpioInterface) StaticInfo() interfaces.StaticInfo {
    58  	return interfaces.StaticInfo{
    59  		Summary:              gpioSummary,
    60  		BaseDeclarationSlots: gpioBaseDeclarationSlots,
    61  	}
    62  }
    63  
    64  // BeforePrepareSlot checks the slot definition is valid
    65  func (iface *gpioInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
    66  	// Must have a GPIO number
    67  	number, ok := slot.Attrs["number"]
    68  	if !ok {
    69  		return fmt.Errorf("gpio slot must have a number attribute")
    70  	}
    71  
    72  	// Valid values of number
    73  	if _, ok := number.(int64); !ok {
    74  		return fmt.Errorf("gpio slot number attribute must be an int")
    75  	}
    76  
    77  	// Slot is good
    78  	return nil
    79  }
    80  
    81  func (iface *gpioInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
    82  	var number int64
    83  	if err := slot.Attr("number", &number); err != nil {
    84  		return err
    85  	}
    86  	path := fmt.Sprint(gpioSysfsGpioBase, number)
    87  	// Entries in /sys/class/gpio for single GPIO's are just symlinks
    88  	// to their correct device part in the sysfs tree. Given AppArmor
    89  	// requires symlinks to be dereferenced, evaluate the GPIO
    90  	// path and add the correct absolute path to the AppArmor snippet.
    91  	dereferencedPath, err := evalSymlinks(path)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	spec.AddSnippet(fmt.Sprintf("%s/* rwk,", dereferencedPath))
    96  	return nil
    97  
    98  }
    99  
   100  func (iface *gpioInterface) SystemdConnectedSlot(spec *systemd.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   101  	var gpioNum int64
   102  	if err := slot.Attr("number", &gpioNum); err != nil {
   103  		return err
   104  	}
   105  
   106  	serviceName := interfaces.InterfaceServiceName(slot.Snap().InstanceName(), fmt.Sprintf("gpio-%d", gpioNum))
   107  	service := &systemd.Service{
   108  		Type:            "oneshot",
   109  		RemainAfterExit: true,
   110  		ExecStart:       fmt.Sprintf("/bin/sh -c 'test -e /sys/class/gpio/gpio%d || echo %d > /sys/class/gpio/export'", gpioNum, gpioNum),
   111  		ExecStop:        fmt.Sprintf("/bin/sh -c 'test ! -e /sys/class/gpio/gpio%d || echo %d > /sys/class/gpio/unexport'", gpioNum, gpioNum),
   112  	}
   113  	return spec.AddService(serviceName, service)
   114  }
   115  
   116  func (iface *gpioInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool {
   117  	// allow what declarations allowed
   118  	return true
   119  }
   120  
   121  func init() {
   122  	registerIface(&gpioInterface{})
   123  }