github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/interfaces/builtin/spi.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 "path/filepath" 25 "regexp" 26 "strings" 27 28 "github.com/snapcore/snapd/interfaces" 29 "github.com/snapcore/snapd/interfaces/apparmor" 30 "github.com/snapcore/snapd/interfaces/udev" 31 "github.com/snapcore/snapd/snap" 32 ) 33 34 const spiSummary = `allows access to specific spi controller` 35 36 const spiBaseDeclarationSlots = ` 37 spi: 38 allow-installation: 39 slot-snap-type: 40 - core 41 - gadget 42 deny-auto-connection: true 43 ` 44 45 type spiInterface struct{} 46 47 func (iface *spiInterface) Name() string { 48 return "spi" 49 } 50 51 func (iface *spiInterface) StaticInfo() interfaces.StaticInfo { 52 return interfaces.StaticInfo{ 53 Summary: spiSummary, 54 BaseDeclarationSlots: spiBaseDeclarationSlots, 55 } 56 } 57 58 var spiDevPattern = regexp.MustCompile(`^/dev/spidev[0-9]+\.[0-9]+$`) 59 60 func (iface *spiInterface) path(slotRef *interfaces.SlotRef, attrs interfaces.Attrer) (string, error) { 61 var path string 62 if err := attrs.Attr("path", &path); err != nil || path == "" { 63 return "", fmt.Errorf("slot %q must have a path attribute", slotRef) 64 } 65 // XXX: this interface feeds the cleaned path into the regex and is 66 // left unchanged here for historical reasons. New interfaces (eg, 67 // like raw-volume) should instead use verifySlotPathAttribute() which 68 // performs additional verification. 69 path = filepath.Clean(path) 70 if !spiDevPattern.MatchString(path) { 71 return "", fmt.Errorf("%q is not a valid SPI device", path) 72 } 73 return path, nil 74 } 75 76 func (iface *spiInterface) BeforePrepareSlot(slot *snap.SlotInfo) error { 77 _, err := iface.path(&interfaces.SlotRef{Snap: slot.Snap.InstanceName(), Name: slot.Name}, slot) 78 return err 79 } 80 81 func (iface *spiInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 82 path, err := iface.path(slot.Ref(), slot) 83 if err != nil { 84 return nil 85 } 86 spec.AddSnippet(fmt.Sprintf("%s rw,", path)) 87 // Use parametric snippets to avoid no-expr-simplify side-effects. 88 spec.AddParametricSnippet([]string{ 89 "/sys/devices/platform/**/**.spi/**/spidev" /* ###PARAM### */, "/** rw, # Add any condensed parametric rules", 90 }, strings.TrimPrefix(path, "/dev/spidev")) 91 return nil 92 } 93 94 func (iface *spiInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 95 path, err := iface.path(slot.Ref(), slot) 96 if err != nil { 97 return nil 98 } 99 spec.TagDevice(fmt.Sprintf(`KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/"))) 100 return nil 101 } 102 103 func (iface *spiInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool { 104 // Allow what is allowed in the declarations 105 return true 106 } 107 108 func init() { 109 registerIface(&spiInterface{}) 110 }