From 81600154f0c1dd73bf8c420d64dec12a19cd9414 Mon Sep 17 00:00:00 2001 From: Markus Pesch Date: Tue, 4 Dec 2018 19:11:50 +0100 Subject: [PATCH] add: humidity (WIP) --- Gopkg.lock | 51 +- cmd/cmd.go | 2 + cmd/humidity/get.go | 28 + cmd/humidity/humidity.go | 20 + pkg/humidity/humidity.go | 30 + pkg/logs/temperature.go | 2 +- .../go-flucky-server/pkg/types/types.go | 93 +- vendor/github.com/MichaelS11/go-dht/LICENSE | 21 + vendor/github.com/MichaelS11/go-dht/README.md | 144 ++ vendor/github.com/MichaelS11/go-dht/dht.go | 196 +++ .../MichaelS11/go-dht/dhtNotWindows.go | 155 ++ .../MichaelS11/go-dht/dhtWindows.go | 35 + .../github.com/MichaelS11/go-dht/globals.go | 27 + vendor/periph.io/x/periph/.gitignore | 1 + vendor/periph.io/x/periph/.gohci.yml | 227 +++ vendor/periph.io/x/periph/.travis.yml | 25 + vendor/periph.io/x/periph/AUTHORS | 15 + vendor/periph.io/x/periph/CONTRIBUTING.md | 4 + vendor/periph.io/x/periph/CONTRIBUTORS | 40 + vendor/periph.io/x/periph/LICENSE | 202 +++ vendor/periph.io/x/periph/Makefile | 59 + vendor/periph.io/x/periph/README.md | 59 + vendor/periph.io/x/periph/conn/conn.go | 103 ++ vendor/periph.io/x/periph/conn/doc.go | 63 + .../periph.io/x/periph/conn/duplex_string.go | 3 + vendor/periph.io/x/periph/conn/gpio/func.go | 25 + vendor/periph.io/x/periph/conn/gpio/gpio.go | 329 ++++ .../x/periph/conn/gpio/gpioreg/gpioreg.go | 213 +++ .../x/periph/conn/gpio/gpioreg/natsort.go | 76 + .../periph/conn/gpio/gpiostream/gpiostream.go | 217 +++ vendor/periph.io/x/periph/conn/i2c/func.go | 12 + vendor/periph.io/x/periph/conn/i2c/i2c.go | 114 ++ .../x/periph/conn/i2c/i2creg/i2creg.go | 252 +++ vendor/periph.io/x/periph/conn/physic/doc.go | 9 + .../periph.io/x/periph/conn/physic/physic.go | 42 + .../periph.io/x/periph/conn/physic/units.go | 1043 +++++++++++++ vendor/periph.io/x/periph/conn/pin/func.go | 58 + vendor/periph.io/x/periph/conn/pin/pin.go | 139 ++ .../periph.io/x/periph/conn/pin/pinreg/doc.go | 7 + .../x/periph/conn/pin/pinreg/pinreg.go | 148 ++ vendor/periph.io/x/periph/conn/spi/func.go | 14 + vendor/periph.io/x/periph/conn/spi/spi.go | 187 +++ .../x/periph/conn/spi/spireg/spireg.go | 262 ++++ .../periph.io/x/periph/host/allwinner/a20.go | 220 +++ .../periph.io/x/periph/host/allwinner/a64.go | 174 +++ .../x/periph/host/allwinner/allwinner_arm.go | 7 + .../periph/host/allwinner/allwinner_arm64.go | 9 + .../periph/host/allwinner/allwinner_other.go | 9 + .../x/periph/host/allwinner/clock.go | 281 ++++ .../x/periph/host/allwinner/detect.go | 98 ++ .../periph.io/x/periph/host/allwinner/dma.go | 474 ++++++ .../periph.io/x/periph/host/allwinner/doc.go | 33 + .../periph.io/x/periph/host/allwinner/gpio.go | 1065 +++++++++++++ .../x/periph/host/allwinner/gpio_pl.go | 563 +++++++ .../periph.io/x/periph/host/allwinner/pwm.go | 197 +++ .../periph.io/x/periph/host/allwinner/r8.go | 149 ++ .../periph.io/x/periph/host/allwinner/spi.go | 207 +++ .../x/periph/host/allwinner/timer.go | 128 ++ .../periph.io/x/periph/host/am335x/am335x.go | 52 + .../x/periph/host/am335x/am335x_arm.go | 7 + .../x/periph/host/am335x/am335x_other.go | 9 + vendor/periph.io/x/periph/host/am335x/doc.go | 28 + .../x/periph/host/bcm283x/bcm283x_arm.go | 7 + .../x/periph/host/bcm283x/bcm283x_arm64.go | 9 + .../x/periph/host/bcm283x/bcm283x_other.go | 9 + .../periph.io/x/periph/host/bcm283x/clock.go | 330 ++++ vendor/periph.io/x/periph/host/bcm283x/dma.go | 1224 +++++++++++++++ vendor/periph.io/x/periph/host/bcm283x/doc.go | 42 + .../periph.io/x/periph/host/bcm283x/gpio.go | 1361 +++++++++++++++++ vendor/periph.io/x/periph/host/bcm283x/pcm.go | 234 +++ vendor/periph.io/x/periph/host/bcm283x/pwm.go | 272 ++++ .../x/periph/host/bcm283x/streams.go | 134 ++ .../periph.io/x/periph/host/bcm283x/timer.go | 60 + .../x/periph/host/beagle/black/black.go | 34 + .../x/periph/host/beagle/black/black_arm.go | 7 + .../x/periph/host/beagle/black/black_other.go | 9 + .../x/periph/host/beagle/bone/bone.go | 318 ++++ .../x/periph/host/beagle/bone/bone_arm.go | 7 + .../x/periph/host/beagle/bone/bone_other.go | 9 + .../x/periph/host/beagle/green/green.go | 95 ++ .../x/periph/host/beagle/green/green_arm.go | 7 + .../x/periph/host/beagle/green/green_other.go | 9 + vendor/periph.io/x/periph/host/chip/chip.go | 358 +++++ .../periph.io/x/periph/host/chip/chip_arm.go | 7 + .../x/periph/host/chip/chip_other.go | 9 + vendor/periph.io/x/periph/host/chip/doc.go | 30 + vendor/periph.io/x/periph/host/cpu/cpu.go | 83 + .../periph.io/x/periph/host/cpu/cpu_linux.go | 22 + .../periph.io/x/periph/host/cpu/cpu_other.go | 14 + vendor/periph.io/x/periph/host/cpu/doc.go | 6 + .../periph.io/x/periph/host/distro/devtree.go | 61 + .../periph.io/x/periph/host/distro/distro.go | 189 +++ .../x/periph/host/distro/distro_arm.go | 7 + .../x/periph/host/distro/distro_arm64.go | 9 + .../x/periph/host/distro/distro_linux.go | 7 + .../x/periph/host/distro/distro_nonarm.go | 9 + .../x/periph/host/distro/distro_nonlinux.go | 9 + vendor/periph.io/x/periph/host/doc.go | 10 + vendor/periph.io/x/periph/host/fs/fs.go | 130 ++ vendor/periph.io/x/periph/host/fs/fs_linux.go | 57 + vendor/periph.io/x/periph/host/fs/fs_other.go | 25 + .../x/periph/host/fs/ioctl_mips_like.go | 9 + .../periph.io/x/periph/host/fs/ioctl_other.go | 9 + vendor/periph.io/x/periph/host/host.go | 15 + vendor/periph.io/x/periph/host/host_arm.go | 20 + vendor/periph.io/x/periph/host/host_arm64.go | 13 + vendor/periph.io/x/periph/host/host_linux.go | 10 + .../periph.io/x/periph/host/odroidc1/doc.go | 26 + .../x/periph/host/odroidc1/odroidc1.go | 195 +++ .../x/periph/host/odroidc1/odroidc1_arm.go | 7 + .../x/periph/host/odroidc1/odroidc1_other.go | 9 + vendor/periph.io/x/periph/host/pine64/doc.go | 15 + .../periph.io/x/periph/host/pine64/pine64.go | 272 ++++ .../x/periph/host/pine64/pine64_arm.go | 7 + .../x/periph/host/pine64/pine64_arm64.go | 9 + .../x/periph/host/pine64/pine64_other.go | 9 + vendor/periph.io/x/periph/host/pmem/alloc.go | 164 ++ vendor/periph.io/x/periph/host/pmem/doc.go | 69 + .../periph.io/x/periph/host/pmem/mem_linux.go | 56 + .../periph.io/x/periph/host/pmem/mem_other.go | 30 + .../periph.io/x/periph/host/pmem/pagemap.go | 64 + .../periph.io/x/periph/host/pmem/smoketest.go | 89 ++ vendor/periph.io/x/periph/host/pmem/view.go | 283 ++++ vendor/periph.io/x/periph/host/rpi/doc.go | 15 + vendor/periph.io/x/periph/host/rpi/rpi.go | 657 ++++++++ vendor/periph.io/x/periph/host/rpi/rpi_arm.go | 7 + .../periph.io/x/periph/host/rpi/rpi_arm64.go | 9 + .../periph.io/x/periph/host/rpi/rpi_other.go | 9 + vendor/periph.io/x/periph/host/sysfs/doc.go | 13 + vendor/periph.io/x/periph/host/sysfs/gpio.go | 515 +++++++ vendor/periph.io/x/periph/host/sysfs/i2c.go | 388 +++++ vendor/periph.io/x/periph/host/sysfs/led.go | 267 ++++ vendor/periph.io/x/periph/host/sysfs/spi.go | 651 ++++++++ vendor/periph.io/x/periph/host/sysfs/sysfs.go | 62 + .../x/periph/host/sysfs/sysfs_linux.go | 17 + .../x/periph/host/sysfs/sysfs_other.go | 14 + .../x/periph/host/sysfs/thermal_sensor.go | 199 +++ .../x/periph/host/videocore/videocore.go | 228 +++ vendor/periph.io/x/periph/periph.go | 298 ++++ vendor/periph.io/x/periph/periph_parallel.go | 101 ++ vendor/periph.io/x/periph/periph_serial.go | 55 + 141 files changed, 18562 insertions(+), 4 deletions(-) create mode 100644 cmd/humidity/get.go create mode 100644 cmd/humidity/humidity.go create mode 100644 pkg/humidity/humidity.go create mode 100644 vendor/github.com/MichaelS11/go-dht/LICENSE create mode 100644 vendor/github.com/MichaelS11/go-dht/README.md create mode 100644 vendor/github.com/MichaelS11/go-dht/dht.go create mode 100644 vendor/github.com/MichaelS11/go-dht/dhtNotWindows.go create mode 100644 vendor/github.com/MichaelS11/go-dht/dhtWindows.go create mode 100644 vendor/github.com/MichaelS11/go-dht/globals.go create mode 100644 vendor/periph.io/x/periph/.gitignore create mode 100644 vendor/periph.io/x/periph/.gohci.yml create mode 100644 vendor/periph.io/x/periph/.travis.yml create mode 100644 vendor/periph.io/x/periph/AUTHORS create mode 100644 vendor/periph.io/x/periph/CONTRIBUTING.md create mode 100644 vendor/periph.io/x/periph/CONTRIBUTORS create mode 100644 vendor/periph.io/x/periph/LICENSE create mode 100644 vendor/periph.io/x/periph/Makefile create mode 100644 vendor/periph.io/x/periph/README.md create mode 100644 vendor/periph.io/x/periph/conn/conn.go create mode 100644 vendor/periph.io/x/periph/conn/doc.go create mode 100644 vendor/periph.io/x/periph/conn/duplex_string.go create mode 100644 vendor/periph.io/x/periph/conn/gpio/func.go create mode 100644 vendor/periph.io/x/periph/conn/gpio/gpio.go create mode 100644 vendor/periph.io/x/periph/conn/gpio/gpioreg/gpioreg.go create mode 100644 vendor/periph.io/x/periph/conn/gpio/gpioreg/natsort.go create mode 100644 vendor/periph.io/x/periph/conn/gpio/gpiostream/gpiostream.go create mode 100644 vendor/periph.io/x/periph/conn/i2c/func.go create mode 100644 vendor/periph.io/x/periph/conn/i2c/i2c.go create mode 100644 vendor/periph.io/x/periph/conn/i2c/i2creg/i2creg.go create mode 100644 vendor/periph.io/x/periph/conn/physic/doc.go create mode 100644 vendor/periph.io/x/periph/conn/physic/physic.go create mode 100644 vendor/periph.io/x/periph/conn/physic/units.go create mode 100644 vendor/periph.io/x/periph/conn/pin/func.go create mode 100644 vendor/periph.io/x/periph/conn/pin/pin.go create mode 100644 vendor/periph.io/x/periph/conn/pin/pinreg/doc.go create mode 100644 vendor/periph.io/x/periph/conn/pin/pinreg/pinreg.go create mode 100644 vendor/periph.io/x/periph/conn/spi/func.go create mode 100644 vendor/periph.io/x/periph/conn/spi/spi.go create mode 100644 vendor/periph.io/x/periph/conn/spi/spireg/spireg.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/a20.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/a64.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/allwinner_arm.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/allwinner_arm64.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/allwinner_other.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/clock.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/detect.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/dma.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/doc.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/gpio.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/gpio_pl.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/pwm.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/r8.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/spi.go create mode 100644 vendor/periph.io/x/periph/host/allwinner/timer.go create mode 100644 vendor/periph.io/x/periph/host/am335x/am335x.go create mode 100644 vendor/periph.io/x/periph/host/am335x/am335x_arm.go create mode 100644 vendor/periph.io/x/periph/host/am335x/am335x_other.go create mode 100644 vendor/periph.io/x/periph/host/am335x/doc.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm64.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/bcm283x_other.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/clock.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/dma.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/doc.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/gpio.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/pcm.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/pwm.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/streams.go create mode 100644 vendor/periph.io/x/periph/host/bcm283x/timer.go create mode 100644 vendor/periph.io/x/periph/host/beagle/black/black.go create mode 100644 vendor/periph.io/x/periph/host/beagle/black/black_arm.go create mode 100644 vendor/periph.io/x/periph/host/beagle/black/black_other.go create mode 100644 vendor/periph.io/x/periph/host/beagle/bone/bone.go create mode 100644 vendor/periph.io/x/periph/host/beagle/bone/bone_arm.go create mode 100644 vendor/periph.io/x/periph/host/beagle/bone/bone_other.go create mode 100644 vendor/periph.io/x/periph/host/beagle/green/green.go create mode 100644 vendor/periph.io/x/periph/host/beagle/green/green_arm.go create mode 100644 vendor/periph.io/x/periph/host/beagle/green/green_other.go create mode 100644 vendor/periph.io/x/periph/host/chip/chip.go create mode 100644 vendor/periph.io/x/periph/host/chip/chip_arm.go create mode 100644 vendor/periph.io/x/periph/host/chip/chip_other.go create mode 100644 vendor/periph.io/x/periph/host/chip/doc.go create mode 100644 vendor/periph.io/x/periph/host/cpu/cpu.go create mode 100644 vendor/periph.io/x/periph/host/cpu/cpu_linux.go create mode 100644 vendor/periph.io/x/periph/host/cpu/cpu_other.go create mode 100644 vendor/periph.io/x/periph/host/cpu/doc.go create mode 100644 vendor/periph.io/x/periph/host/distro/devtree.go create mode 100644 vendor/periph.io/x/periph/host/distro/distro.go create mode 100644 vendor/periph.io/x/periph/host/distro/distro_arm.go create mode 100644 vendor/periph.io/x/periph/host/distro/distro_arm64.go create mode 100644 vendor/periph.io/x/periph/host/distro/distro_linux.go create mode 100644 vendor/periph.io/x/periph/host/distro/distro_nonarm.go create mode 100644 vendor/periph.io/x/periph/host/distro/distro_nonlinux.go create mode 100644 vendor/periph.io/x/periph/host/doc.go create mode 100644 vendor/periph.io/x/periph/host/fs/fs.go create mode 100644 vendor/periph.io/x/periph/host/fs/fs_linux.go create mode 100644 vendor/periph.io/x/periph/host/fs/fs_other.go create mode 100644 vendor/periph.io/x/periph/host/fs/ioctl_mips_like.go create mode 100644 vendor/periph.io/x/periph/host/fs/ioctl_other.go create mode 100644 vendor/periph.io/x/periph/host/host.go create mode 100644 vendor/periph.io/x/periph/host/host_arm.go create mode 100644 vendor/periph.io/x/periph/host/host_arm64.go create mode 100644 vendor/periph.io/x/periph/host/host_linux.go create mode 100644 vendor/periph.io/x/periph/host/odroidc1/doc.go create mode 100644 vendor/periph.io/x/periph/host/odroidc1/odroidc1.go create mode 100644 vendor/periph.io/x/periph/host/odroidc1/odroidc1_arm.go create mode 100644 vendor/periph.io/x/periph/host/odroidc1/odroidc1_other.go create mode 100644 vendor/periph.io/x/periph/host/pine64/doc.go create mode 100644 vendor/periph.io/x/periph/host/pine64/pine64.go create mode 100644 vendor/periph.io/x/periph/host/pine64/pine64_arm.go create mode 100644 vendor/periph.io/x/periph/host/pine64/pine64_arm64.go create mode 100644 vendor/periph.io/x/periph/host/pine64/pine64_other.go create mode 100644 vendor/periph.io/x/periph/host/pmem/alloc.go create mode 100644 vendor/periph.io/x/periph/host/pmem/doc.go create mode 100644 vendor/periph.io/x/periph/host/pmem/mem_linux.go create mode 100644 vendor/periph.io/x/periph/host/pmem/mem_other.go create mode 100644 vendor/periph.io/x/periph/host/pmem/pagemap.go create mode 100644 vendor/periph.io/x/periph/host/pmem/smoketest.go create mode 100644 vendor/periph.io/x/periph/host/pmem/view.go create mode 100644 vendor/periph.io/x/periph/host/rpi/doc.go create mode 100644 vendor/periph.io/x/periph/host/rpi/rpi.go create mode 100644 vendor/periph.io/x/periph/host/rpi/rpi_arm.go create mode 100644 vendor/periph.io/x/periph/host/rpi/rpi_arm64.go create mode 100644 vendor/periph.io/x/periph/host/rpi/rpi_other.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/doc.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/gpio.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/i2c.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/led.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/spi.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/sysfs.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/sysfs_linux.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/sysfs_other.go create mode 100644 vendor/periph.io/x/periph/host/sysfs/thermal_sensor.go create mode 100644 vendor/periph.io/x/periph/host/videocore/videocore.go create mode 100644 vendor/periph.io/x/periph/periph.go create mode 100644 vendor/periph.io/x/periph/periph_parallel.go create mode 100644 vendor/periph.io/x/periph/periph_serial.go diff --git a/Gopkg.lock b/Gopkg.lock index 8ee2cb1..41f2e8f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,11 +3,19 @@ [[projects]] branch = "master" - digest = "1:ab8875db0749c2b2d147b0d250b73e45a2a722da4dd3664729ac207f1e6dcf9c" + digest = "1:36cccfe52f1b5332dd9374abca00bdb31b99a757c15b8a2b103f3f7b4541816b" name = "git.cryptic.systems/fh-trier/go-flucky-server" packages = ["pkg/types"] pruneopts = "UT" - revision = "09990bcc64b4c240c53c0e98c03add895c1902c1" + revision = "3ce11606f75951619536ec3e3e501bc31dc53dd7" + +[[projects]] + branch = "master" + digest = "1:2afcd3229edbe02821d3b2fc69ebd0e31365ad7f98ee88a08abb69d2c92b48be" + name = "github.com/MichaelS11/go-dht" + packages = ["."] + pruneopts = "UT" + revision = "be44b9ee7fec8f81d57dea89c17d26961183266e" [[projects]] digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" @@ -49,11 +57,50 @@ pruneopts = "UT" revision = "3cf383a406247c49e48a80014f3e17ddee418742" +[[projects]] + digest = "1:9ad50a8dc9edeed28acefe7117cae2de893b5fbbbe551ebfad26d56304cc83d8" + name = "periph.io/x/periph" + packages = [ + ".", + "conn", + "conn/gpio", + "conn/gpio/gpioreg", + "conn/gpio/gpiostream", + "conn/i2c", + "conn/i2c/i2creg", + "conn/physic", + "conn/pin", + "conn/pin/pinreg", + "conn/spi", + "conn/spi/spireg", + "host", + "host/allwinner", + "host/am335x", + "host/bcm283x", + "host/beagle/black", + "host/beagle/bone", + "host/beagle/green", + "host/chip", + "host/cpu", + "host/distro", + "host/fs", + "host/odroidc1", + "host/pine64", + "host/pmem", + "host/rpi", + "host/sysfs", + "host/videocore", + ] + pruneopts = "UT" + revision = "f44fc121a350041e7a23acc3ccf87edc8780f3ba" + version = "v3.3.0" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "git.cryptic.systems/fh-trier/go-flucky-server/pkg/types", + "github.com/MichaelS11/go-dht", "github.com/satori/go.uuid", "github.com/spf13/cobra", "github.com/yryz/ds18b20", diff --git a/cmd/cmd.go b/cmd/cmd.go index 452f462..6d758f5 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -2,6 +2,7 @@ package cmd import ( "git.cryptic.systems/fh-trier/go-flucky/cmd/config" + "git.cryptic.systems/fh-trier/go-flucky/cmd/humidity" "git.cryptic.systems/fh-trier/go-flucky/cmd/remote" "git.cryptic.systems/fh-trier/go-flucky/cmd/sensor" "git.cryptic.systems/fh-trier/go-flucky/cmd/temperature" @@ -22,6 +23,7 @@ func Execute(version string) { rootCmd.PersistentFlags().StringVarP(&configDir, "config", "c", "/etc/flucky", "The base directory for all configuration files.") config.InitCmd(rootCmd, configDir) + humidity.InitCmd(rootCmd, configDir) remote.InitCmd(rootCmd, configDir) sensor.InitCmd(rootCmd, configDir) temperature.InitCmd(rootCmd, configDir) diff --git a/cmd/humidity/get.go b/cmd/humidity/get.go new file mode 100644 index 0000000..c052a09 --- /dev/null +++ b/cmd/humidity/get.go @@ -0,0 +1,28 @@ +package humidity + +import ( + "log" + + "git.cryptic.systems/fh-trier/go-flucky/pkg/humidity" + "github.com/spf13/cobra" +) + +var follow, push bool + +var getHumidityCmd = &cobra.Command{ + Use: "get", + Short: "get humidity from sensor", + Run: func(cmd *cobra.Command, args []string) { + + if err := humidity.Get(); err != nil { + log.Fatal(err) + } + + }, +} + +func init() { + humidityCmd.AddCommand(getHumidityCmd) + // getTemperatureCmd.Flags().BoolVarP(&follow, "follow", "f", false, "Follow output") + // getTemperatureCmd.Flags().BoolVarP(&push, "push", "p", false, "Push to remote server") +} diff --git a/cmd/humidity/humidity.go b/cmd/humidity/humidity.go new file mode 100644 index 0000000..5d6de3d --- /dev/null +++ b/cmd/humidity/humidity.go @@ -0,0 +1,20 @@ +package humidity + +import ( + "github.com/spf13/cobra" +) + +var configDir string + +var humidityCmd = &cobra.Command{ + Use: "humidity", + Short: "Read humidity from sensor", +} + +// Execute a +func InitCmd(cmd *cobra.Command, cnf string) { + configDir = cnf + + cmd.AddCommand(humidityCmd) + +} diff --git a/pkg/humidity/humidity.go b/pkg/humidity/humidity.go new file mode 100644 index 0000000..d0692c7 --- /dev/null +++ b/pkg/humidity/humidity.go @@ -0,0 +1,30 @@ +package humidity + +import ( + "fmt" + + dht "github.com/MichaelS11/go-dht" +) + +func Get() error { + err := dht.HostInit() + if err != nil { + return fmt.Errorf("HostInit error: %v", err) + } + + dht, err := dht.NewDHT("GPIO19", dht.Celsius, "dht11") + if err != nil { + return fmt.Errorf("NewDHT error: %v", err) + + } + + humidity, temperature, err := dht.ReadRetry(10) + if err != nil { + return fmt.Errorf("Read error: %v", err) + } + + fmt.Printf("humidity: %v\n", humidity) + fmt.Printf("temperature: %v\n", temperature) + + return nil +} diff --git a/pkg/logs/temperature.go b/pkg/logs/temperature.go index f5d2d19..91aecf4 100644 --- a/pkg/logs/temperature.go +++ b/pkg/logs/temperature.go @@ -85,7 +85,7 @@ func ListTemperatures(configDir string, w io.Writer) error { // print headline fmt.Fprint(tw, "\t") for _, sensor := range sensors { - fmt.Fprintf(tw, "%v\t", sensor.HumanName()) + fmt.Fprintf(tw, "%v\t", sensor.ReadableSensorName()) } if len(sensors) > 0 { fmt.Fprint(tw, "\n") diff --git a/vendor/git.cryptic.systems/fh-trier/go-flucky-server/pkg/types/types.go b/vendor/git.cryptic.systems/fh-trier/go-flucky-server/pkg/types/types.go index 301f75c..d91aa06 100644 --- a/vendor/git.cryptic.systems/fh-trier/go-flucky-server/pkg/types/types.go +++ b/vendor/git.cryptic.systems/fh-trier/go-flucky-server/pkg/types/types.go @@ -36,6 +36,36 @@ func (d *Device) DecodeFromJSON(r io.Reader) error { return nil } +// Failnoise ... +type Failnoise struct { + FailnoiseID string `json:"failnoise_id"` + FailnoiseCounter int `json:"failnoise_counter,string"` + FailnoiseStartDate time.Time `json:"failnoise_start_date"` + FailnoiseEndDate time.Time `json:"failnoise_end_date"` + SensorID string `json:"sensor_id"` + CreationDate time.Time `json:"creation_date"` +} + +// EncodeToJSON needs a writer to write the struct into json string +func (f *Failnoise) EncodeToJSON(w io.Writer) error { + encoder := json.NewEncoder(w) + encoder.SetIndent("", " ") + err := encoder.Encode(&f) + if err != nil { + return fmt.Errorf("Error in encoding struct to json: %v", err) + } + return nil +} + +// DecodeFromJSON decode a json string from a reader into a struct +func (f *Failnoise) DecodeFromJSON(r io.Reader) error { + decoder := json.NewDecoder(r) + if err := decoder.Decode(&f); err != nil { + return fmt.Errorf("Can not unmarshal JSON: %v", err) + } + return nil +} + // Humidity ... type Humidity struct { HumidityID string `json:"humidity_id"` @@ -65,6 +95,36 @@ func (h *Humidity) DecodeFromJSON(r io.Reader) error { return nil } +// Knock ... +type Knock struct { + KnockID string `json:"knock_id"` + KnockCounter int `json:"knock_counter,string"` + KnockStartDate time.Time `json:"knock_start_date"` + KnockEndDate time.Time `json:"knock_end_date"` + SensorID string `json:"sensor_id"` + CreationDate time.Time `json:"creation_date"` +} + +// EncodeToJSON needs a writer to write the struct into json string +func (k *Knock) EncodeToJSON(w io.Writer) error { + encoder := json.NewEncoder(w) + encoder.SetIndent("", " ") + err := encoder.Encode(&k) + if err != nil { + return fmt.Errorf("Error in encoding struct to json: %v", err) + } + return nil +} + +// DecodeFromJSON decode a json string from a reader into a struct +func (k *Knock) DecodeFromJSON(r io.Reader) error { + decoder := json.NewDecoder(r) + if err := decoder.Decode(&k); err != nil { + return fmt.Errorf("Can not unmarshal JSON: %v", err) + } + return nil +} + // Sensor ... type Sensor struct { SensorID string `json:"sensor_id"` @@ -99,7 +159,8 @@ func (s *Sensor) DecodeFromJSON(r io.Reader) error { return nil } -func (s *Sensor) HumanName() string { +// ReadableSensorName returns a readable sensor name +func (s *Sensor) ReadableSensorName() string { if s.SensorName != nil { return *s.SensorName } @@ -137,3 +198,33 @@ func (t *Temperature) DecodeFromJSON(r io.Reader) error { } return nil } + +// Throughput ... +type Throughput struct { + ThroughputID string `json:"throughput_id"` + ThroughputAverage float64 `json:"throughput_average,string"` + ThroughputStartDate time.Time `json:"throughput_start_date"` + ThroughputEndDate time.Time `json:"throughput_end_date"` + SensorID string `json:"sensor_id"` + CreationDate time.Time `json:"creation_date"` +} + +// EncodeToJSON needs a writer to write the struct into json string +func (t *Throughput) EncodeToJSON(w io.Writer) error { + encoder := json.NewEncoder(w) + encoder.SetIndent("", " ") + err := encoder.Encode(&t) + if err != nil { + return fmt.Errorf("Error in encoding struct to json: %v", err) + } + return nil +} + +// DecodeFromJSON decode a json string from a reader into a struct +func (t *Throughput) DecodeFromJSON(r io.Reader) error { + decoder := json.NewDecoder(r) + if err := decoder.Decode(&t); err != nil { + return fmt.Errorf("Can not unmarshal JSON: %v", err) + } + return nil +} diff --git a/vendor/github.com/MichaelS11/go-dht/LICENSE b/vendor/github.com/MichaelS11/go-dht/LICENSE new file mode 100644 index 0000000..92e3a86 --- /dev/null +++ b/vendor/github.com/MichaelS11/go-dht/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 MichaelS11 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/MichaelS11/go-dht/README.md b/vendor/github.com/MichaelS11/go-dht/README.md new file mode 100644 index 0000000..c06ab70 --- /dev/null +++ b/vendor/github.com/MichaelS11/go-dht/README.md @@ -0,0 +1,144 @@ +# Go DHT22 / AM2302 / DHT11 interface + +Golang DHT22 / AM2302 / DHT11 interface using periph.io driver + +[![GoDoc Reference](https://godoc.org/github.com/MichaelS11/go-dht?status.svg)](http://godoc.org/github.com/MichaelS11/go-dht) +[![Go Report Card](https://goreportcard.com/badge/github.com/MichaelS11/go-dht)](https://goreportcard.com/report/github.com/MichaelS11/go-dht) + + +## Please note + +Please make sure to setup your DHT22 / AM2302 / DHT11 correctly. Do a search on the internet to find guide. Here is an example of a guide: + +https://learn.adafruit.com/dht/connecting-to-a-dhtxx-sensor + +The examples below are from using a Raspberry Pi 3 with GPIO 19 for the pin. Your setup may be different, if so, your pin names would need to change in each example. + +Side note, in my testing the sensor has a fairly high level of read errors, suggest using ReadRetry or ReadBackground rather then just Read. + +Tested on Raspberry Pi 3 with AM2302. Please open an issue if there are any issues. + + +## Get + +go get github.com/MichaelS11/go-dht + + +## ReadRetry example + +```go +package main + +import ( + "fmt" + + "github.com/MichaelS11/go-dht" +) + +func main() { + err := dht.HostInit() + if err != nil { + fmt.Println("HostInit error:", err) + return + } + + dht, err := dht.NewDHT("GPIO19", dht.Fahrenheit, "") + if err != nil { + fmt.Println("NewDHT error:", err) + return + } + + humidity, temperature, err := dht.ReadRetry(11) + if err != nil { + fmt.Println("Read error:", err) + return + } + + fmt.Printf("humidity: %v\n", humidity) + fmt.Printf("temperature: %v\n", temperature) +} +``` + + +## ReadBackground example + +```go +package main + +import ( + "fmt" + "time" + + "github.com/MichaelS11/go-dht" +) + +func main() { + err := dht.HostInit() + if err != nil { + fmt.Println("HostInit error:", err) + return + } + + dht, err := dht.NewDHT("GPIO19", dht.Fahrenheit, "") + if err != nil { + fmt.Println("NewDHT error:", err) + return + } + + stop := make(chan struct{}) + stopped := make(chan struct{}) + var humidity float64 + var temperature float64 + + // get sensor reading every 20 seconds in background + go dht.ReadBackground(&humidity, &temperature, 20*time.Second, stop, stopped) + + // should have at least read the sensor twice after 30 seconds + time.Sleep(30 * time.Second) + + fmt.Printf("humidity: %v\n", humidity) + fmt.Printf("temperature: %v\n", temperature) + + // to stop ReadBackground after done with reading, close the stop channel + close(stop) + + // can check stopped channel to know when ReadBackground has stopped + <-stopped +} +``` + + +## Read example + +```go +package main + +import ( + "fmt" + + "github.com/MichaelS11/go-dht" +) + +func main() { + err := dht.HostInit() + if err != nil { + fmt.Println("HostInit error:", err) + return + } + + dht, err := dht.NewDHT("GPIO19", dht.Fahrenheit, "") + if err != nil { + fmt.Println("NewDHT error:", err) + return + } + + humidity, temperature, err := dht.Read() + if err != nil { + fmt.Println("Read error:", err) + return + } + + fmt.Printf("humidity: %v\n", humidity) + fmt.Printf("temperature: %v\n", temperature) +} +``` diff --git a/vendor/github.com/MichaelS11/go-dht/dht.go b/vendor/github.com/MichaelS11/go-dht/dht.go new file mode 100644 index 0000000..5390597 --- /dev/null +++ b/vendor/github.com/MichaelS11/go-dht/dht.go @@ -0,0 +1,196 @@ +package dht + +import ( + "fmt" + "time" + + "periph.io/x/periph/host" +) + +// HostInit calls periph.io host.Init(). This needs to be done before DHT can be used. +func HostInit() error { + _, err := host.Init() + return err +} + +// Read reads the sensor once, returing humidity and temperature, or an error. +// Note that Read will sleep for at least 2 seconds between last call. +// Each reads error adds a half second to sleep time to max of 30 seconds. +func (dht *DHT) Read() (humidity float64, temperature float64, err error) { + // set sleepTime + var sleepTime time.Duration + if dht.numErrors < 57 { + sleepTime = (2 * time.Second) + (time.Duration(dht.numErrors) * 500 * time.Millisecond) + } else { + // sleep max of 30 seconds + sleepTime = 30 * time.Second + } + sleepTime -= time.Since(dht.lastRead) + + // sleep between 2 and 30 seconds + time.Sleep(sleepTime) + + // read bits from sensor + var bits []int + bits, err = dht.readBits() + if err != nil { + return + } + + // covert bits to humidity and temperature + humidity, temperature, err = dht.bitsToValues(bits) + + return +} + +// bitsToValues will convert the bits into humidity and temperature values +func (dht *DHT) bitsToValues(bits []int) (humidity float64, temperature float64, err error) { + var sum8 uint8 + var sumTotal uint8 + var checkSum uint8 + var i int + var humidityInt int + var temperatureInt int + + // get humidityInt value + for i = 0; i < 16; i++ { + humidityInt = humidityInt << 1 + humidityInt += bits[i] + // sum 8 bits for checkSum + sum8 = sum8 << 1 + sum8 += uint8(bits[i]) + if i == 7 || i == 15 { + // got 8 bits, add to sumTotal for checkSum + sumTotal += sum8 + sum8 = 0 + } + } + + // get temperatureInt value + for i = 16; i < 32; i++ { + temperatureInt = temperatureInt << 1 + temperatureInt += bits[i] + // sum 8 bits for checkSum + sum8 = sum8 << 1 + sum8 += uint8(bits[i]) + if i == 23 || i == 31 { + // got 8 bits, add to sumTotal for checkSum + sumTotal += sum8 + sum8 = 0 + } + } + // if high 16 bit is set, value is negtive + // 1000000000000000 = 0x8000 + if (temperatureInt & 0x8000) > 0 { + // flip bits 16 and lower to get negtive number for int + // 1111111111111111 = 0xffff + temperatureInt |= ^0xffff + } + + // get checkSum value + for i = 32; i < 40; i++ { + checkSum = checkSum << 1 + checkSum += uint8(bits[i]) + } + + if dht.sensorType != "dht11" { + // humidity is between 0 % to 100 % + if humidityInt < 0 || humidityInt > 1000 { + err = fmt.Errorf("bad data - humidity: %v", humidityInt) + return + } + // temperature between -40 C to 80 C + if temperatureInt < -400 || temperatureInt > 800 { + err = fmt.Errorf("bad data - temperature: %v", temperatureInt) + return + } + // check checkSum + if checkSum != sumTotal { + err = fmt.Errorf("bad data - check sum fail") + } + + humidity = float64(humidityInt) / 10.0 + if dht.temperatureUnit == Celsius { + temperature = float64(temperatureInt) / 10.0 + } else { + temperature = float64(temperatureInt)*9.0/50.0 + 32.0 + } + + return + } + + // humidity is between 0 % to 100 % + if humidityInt < 0 || humidityInt > 100 { + err = fmt.Errorf("bad data - humidity: %v", humidityInt) + return + } + // temperature between 0 C to 50 C + if temperatureInt < 0 || temperatureInt > 50 { + err = fmt.Errorf("bad data - temperature: %v", temperatureInt) + return + } + // check checkSum + if checkSum != sumTotal { + err = fmt.Errorf("bad data - check sum fail") + } + + humidity = float64(humidityInt) + if dht.temperatureUnit == Celsius { + temperature = float64(temperatureInt) + } else { + temperature = float64(temperatureInt)*9.0/5.0 + 32.0 + } + + return +} + +// ReadRetry will call Read until there is no errors or the maxRetries is hit. +// Suggest maxRetries to be set around 11. +func (dht *DHT) ReadRetry(maxRetries int) (humidity float64, temperature float64, err error) { + for i := 0; i < maxRetries; i++ { + humidity, temperature, err = dht.Read() + if err == nil { + return + } + } + return +} + +// ReadBackground it meant to be run in the background, run as a Goroutine. +// sleepDuration is how long it will try to sleep between reads. +// If there is ongoing read errors there will be no notice except that the values will not be updated. +// Will continue to read sensor until stop is closed. +// After it has been stopped, the stopped chan will be closed. +// Will panic if humidity, temperature, or stop are nil. +func (dht *DHT) ReadBackground(humidity *float64, temperature *float64, sleepDuration time.Duration, stop chan struct{}, stopped chan struct{}) { + var humidityTemp float64 + var temperatureTemp float64 + var err error + var startTime time.Time + +Loop: + for { + startTime = time.Now() + humidityTemp, temperatureTemp, err = dht.Read() + if err == nil { + // no read error, save result + *humidity = humidityTemp + *temperature = temperatureTemp + // wait for sleepDuration or stop + select { + case <-time.After(sleepDuration - time.Since(startTime)): + case <-stop: + break Loop + } + } else { + // read error, just check for stop + select { + case <-stop: + break Loop + default: + } + } + } + + close(stopped) +} diff --git a/vendor/github.com/MichaelS11/go-dht/dhtNotWindows.go b/vendor/github.com/MichaelS11/go-dht/dhtNotWindows.go new file mode 100644 index 0000000..26e51e7 --- /dev/null +++ b/vendor/github.com/MichaelS11/go-dht/dhtNotWindows.go @@ -0,0 +1,155 @@ +// +build !windows + +package dht + +import ( + "fmt" + "log" + "runtime/debug" + "strings" + "time" + + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" +) + +// NewDHT to create a new DHT struct. +// sensorType is dht11 for DHT11, anything else for AM2302 / DHT22. +func NewDHT(pinName string, temperatureUnit TemperatureUnit, sensorType string) (*DHT, error) { + dht := &DHT{temperatureUnit: temperatureUnit} + + // set sensorType + sensorType = strings.ToLower(sensorType) + if sensorType == "dht11" { + dht.sensorType = "dht11" + } + + // get pin + dht.pin = gpioreg.ByName(pinName) + if dht.pin == nil { + return nil, fmt.Errorf("pin is nill") + } + + // set pin to high so ready for first read + err := dht.pin.Out(gpio.High) + if err != nil { + return nil, fmt.Errorf("pin out high error: %v", err) + } + + // set lastRead a second before to give the pin a second to warm up + dht.lastRead = time.Now().Add(-1 * time.Second) + + return dht, nil +} + +// readBits will get the bits for humidity and temperature +func (dht *DHT) readBits() ([]int, error) { + // create variables ahead of time before critical timing part + var i int + var startTime time.Time + var levelPrevious gpio.Level + var level gpio.Level + levels := make([]gpio.Level, 0, 84) + durations := make([]time.Duration, 0, 84) + + // set lastRead so do not read more than once every 2 seconds + dht.lastRead = time.Now() + + // disable garbage collection during critical timing part + gcPercent := debug.SetGCPercent(-1) + + // send start low + err := dht.pin.Out(gpio.Low) + if err != nil { + dht.pin.Out(gpio.High) + return nil, fmt.Errorf("pin out low error: %v", err) + } + time.Sleep(time.Millisecond) + + // send start high + err = dht.pin.In(gpio.PullUp, gpio.NoEdge) + if err != nil { + dht.pin.Out(gpio.High) + return nil, fmt.Errorf("pin in error: %v", err) + } + + // read levels and durations with busy read + // hope there is a better way in the future + // tried to use WaitForEdge but seems to miss edges and/or take too long to detect them + // note that pin read takes around .2 microsecond (us) on Raspberry PI 3 + // note that 1000 microsecond (us) = 1 millisecond (ms) + levelPrevious = dht.pin.Read() + level = levelPrevious + for i = 0; i < 84; i++ { + startTime = time.Now() + for levelPrevious == level && time.Since(startTime) < time.Millisecond { + level = dht.pin.Read() + } + durations = append(durations, time.Since(startTime)) + levels = append(levels, levelPrevious) + levelPrevious = level + } + + // enable garbage collection, done with critical part + debug.SetGCPercent(gcPercent) + + log.Printf("%v\n", levels) + + // set pin to high so ready for next time + err = dht.pin.Out(gpio.High) + if err != nil { + return nil, fmt.Errorf("pin out high error: %v", err) + } + + // get last low reading so know start of data + var endNumber int + for i = len(levels) - 1; ; i-- { + if levels[i] == gpio.Low { + endNumber = i + break + } + if i < 80 { + // not enough readings, i = 79 means endNumber is 78 or less + return nil, fmt.Errorf("missing some readings - low level not found") + } + } + startNumber := endNumber - 79 + + // covert pulses into bits and check high levels + bits := make([]int, 40) + index := 0 + for i = startNumber; i < endNumber; i += 2 { + // check high levels + if levels[i] != gpio.High { + return nil, fmt.Errorf("missing some readings - level not high") + } + // high should not be longer then 90 microseconds + if durations[i] > 90*time.Microsecond { + return nil, fmt.Errorf("missing some readings - high level duration too long: %v", durations[i]) + } + // bit is 0 if less than or equal to 30 microseconds + if durations[i] > 30*time.Microsecond { + // bit is 1 if more than 30 microseconds + bits[index] = 1 + } + index++ + } + + // check low levels + for i = startNumber + 1; i < endNumber+1; i += 2 { + // check low levels + if levels[i] != gpio.Low { + return nil, fmt.Errorf("missing some readings - level not low") + } + // low should not be longer then 70 microseconds + if durations[i] > 70*time.Microsecond { + return nil, fmt.Errorf("missing some readings - low level duration too long: %v", durations[i]) + } + // low should not be shorter then 35 microseconds + if durations[i] < 35*time.Microsecond { + return nil, fmt.Errorf("missing some readings - low level duration too short: %v", durations[i]) + } + } + + return bits, nil +} diff --git a/vendor/github.com/MichaelS11/go-dht/dhtWindows.go b/vendor/github.com/MichaelS11/go-dht/dhtWindows.go new file mode 100644 index 0000000..28bd333 --- /dev/null +++ b/vendor/github.com/MichaelS11/go-dht/dhtWindows.go @@ -0,0 +1,35 @@ +// +build windows + +package dht + +import ( + "strings" + "time" +) + +// NewDHT to create a new DHT struct. +// sensorType is dht11 for DHT11, anything else for AM2302 / DHT22. +func NewDHT(pinName string, temperatureUnit TemperatureUnit, sensorType string) (*DHT, error) { + dht := &DHT{temperatureUnit: temperatureUnit} + + // set sensorType + sensorType = strings.ToLower(sensorType) + if sensorType == "dht11" { + dht.sensorType = "dht11" + } + + // set lastRead a second before to give the pin a second to warm up + dht.lastRead = time.Now().Add(-1 * time.Second) + + return dht, nil +} + +// readBits will get the bits for humidity and temperature +func (dht *DHT) readBits() ([]int, error) { + // set lastRead so do not read more than once every 2 seconds + dht.lastRead = time.Now() + + bits := make([]int, 40) + + return bits, nil +} diff --git a/vendor/github.com/MichaelS11/go-dht/globals.go b/vendor/github.com/MichaelS11/go-dht/globals.go new file mode 100644 index 0000000..57b8b70 --- /dev/null +++ b/vendor/github.com/MichaelS11/go-dht/globals.go @@ -0,0 +1,27 @@ +package dht + +import ( + "time" + + "periph.io/x/periph/conn/gpio" +) + +// TemperatureUnit is the temperature unit wanted, either Celsius or Fahrenheit +type TemperatureUnit int + +const ( + // Celsius temperature unit + Celsius TemperatureUnit = iota + // Fahrenheit temperature unit + Fahrenheit +) + +// DHT struct to interface with the sensor. +// Call NewDHT to create a new one. +type DHT struct { + pin gpio.PinIO + temperatureUnit TemperatureUnit + sensorType string + numErrors int + lastRead time.Time +} diff --git a/vendor/periph.io/x/periph/.gitignore b/vendor/periph.io/x/periph/.gitignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/vendor/periph.io/x/periph/.gitignore @@ -0,0 +1 @@ +bin diff --git a/vendor/periph.io/x/periph/.gohci.yml b/vendor/periph.io/x/periph/.gohci.yml new file mode 100644 index 0000000..9c0d2e5 --- /dev/null +++ b/vendor/periph.io/x/periph/.gohci.yml @@ -0,0 +1,227 @@ +# See https://github.com/periph/gohci +version: 1 +workers: +- name: beaglebone-4373 + checks: + - cmd: + - go + - install + - -v + - ./cmd/headers-list + - ./cmd/i2c-list + - ./cmd/periph-info + - ./cmd/spi-list + - cmd: + - periph-info + - cmd: + - headers-list + - -f + - cmd: + - i2c-list + - cmd: + - spi-list +- name: chip-a87d + checks: + - cmd: + - go + - install + - -v + - ./cmd/gpio-list + - ./cmd/headers-list + - ./cmd/i2c-list + - ./cmd/periph-info + - ./cmd/periph-smoketest + - ./cmd/spi-list + - cmd: + - periph-info + - cmd: + - gpio-list + - -f + - cmd: + - headers-list + - -f + - cmd: + - i2c-list + - cmd: + - spi-list + - cmd: + - periph-smoketest + - chip + - cmd: + - periph-smoketest + - i2c-testboard + - -bus + - 1 + - cmd: + - periph-smoketest + - onewire-testboard + - -i2cbus + - 1 + - cmd: + - periph-smoketest + - sysfs-benchmark + - -p + - 1013 + - -short + - cmd: + - periph-smoketest + - sysfs-benchmark + - -p + - 132 + - -short + - cmd: + - periph-smoketest + - allwinner-benchmark + - -p + - 132 + - -short + - cmd: + - periph-smoketest + - spi-testboard +- name: mbp + checks: + - cmd: + - go + - test + - -race + - ./... + - cmd: + - go + - install + - -v + - ./cmd/gpio-list + - ./cmd/headers-list + - ./cmd/i2c-list + - ./cmd/periph-info + - ./cmd/spi-list + - cmd: + - periph-info + - cmd: + - gpio-list + - -f + - cmd: + - headers-list + - -f + - cmd: + - i2c-list + - cmd: + - spi-list +- name: odroid-483d + checks: + - cmd: + - go + - test + - -cover + - -bench=. + - -benchtime=1000ms + - -benchmem + - ./... + - ./... + - cmd: + - go + - install + - -v + - ./cmd/gpio-list + - ./cmd/headers-list + - ./cmd/i2c-list + - ./cmd/periph-info + - ./cmd/periph-smoketest + - ./cmd/spi-list + - cmd: + - periph-info + - cmd: + - gpio-list + - -f + - cmd: + - headers-list + - -f + - cmd: + - i2c-list + - cmd: + - spi-list + - cmd: + - periph-smoketest + - odroid-c1 + - cmd: + - periph-smoketest + - i2c-testboard + - cmd: + - periph-smoketest + - onewire-testboard + - cmd: + - periph-smoketest + - spi-testboard + - cmd: + - periph-smoketest + - sysfs-benchmark + - -p + - 97 + - -short +- name: raspberrypi-2f34 + checks: + - cmd: + - go + - install + - -v + - ./cmd/gpio-list + - ./cmd/headers-list + - ./cmd/i2c-list + - ./cmd/periph-info + - ./cmd/periph-smoketest + - ./cmd/spi-list + - cmd: + - periph-info + - cmd: + - gpio-list + - -f + - cmd: + - headers-list + - -f + - cmd: + - i2c-list + - cmd: + - spi-list + - cmd: + - periph-smoketest + - i2c-testboard + - cmd: + - periph-smoketest + - onewire-testboard + - -i2cbus + - 1 + - cmd: + - periph-smoketest + - spi-testboard + - cmd: + - periph-smoketest + - sysfs-benchmark + - -p + - 12 + - -short + - cmd: + - periph-smoketest + - bcm283x-benchmark + - -p + - 12 + - -short +- name: win10 + checks: + - cmd: + - go + - test + - -cover + - -bench=. + - -benchtime=1000ms + - -benchmem + - ./... + - cmd: + - go + - test + - -race + - ./... + - cmd: + - go + - vet + - -all + - -unsafeptr=false + - ./... diff --git a/vendor/periph.io/x/periph/.travis.yml b/vendor/periph.io/x/periph/.travis.yml new file mode 100644 index 0000000..e38c196 --- /dev/null +++ b/vendor/periph.io/x/periph/.travis.yml @@ -0,0 +1,25 @@ +language: go +sudo: false +go_import_path: periph.io/x/periph +go: + - 1.5.4 + - 1.7.6 + - 1.11.1 + +before_script: + - echo $TRAVIS_GO_VERSION + - go get -t -v periph.io/x/periph/... +script: + # Things run only on the latest version. + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then echo 'Check Code is well formatted'; ! gofmt -s -d . | read; fi + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then echo 'Looking for external dependencies:'; go list -f '{{join .Imports "\n"}}' periph.io/x/periph/... | sort | uniq | grep -v ^periph.io/x/periph | xargs go list -f '{{if not .Standard}}- {{.ImportPath}}{{end}}'; fi + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then echo 'Erroring on external dependencies:'; ! go list -f '{{join .Imports "\n"}}' periph.io/x/periph/... | sort | uniq | grep -v ^periph.io/x/periph | xargs go list -f '{{if not .Standard}}Remove {{.ImportPath}}{{end}}' | grep -q Remove; fi + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then echo 'Erroring on /host depending on /devices:'; ! go list -f '{{.ImportPath}} depends on {{join .Imports ", "}}' periph.io/x/periph/host/... | sort | uniq | grep periph.io/x/periph/devices; fi + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then echo 'Erroring on /conn depending on /devices:'; ! go list -f '{{.ImportPath}} depends on {{join .Imports ", "}}' periph.io/x/periph/conn/... | sort | uniq | grep periph.io/x/periph/devices; fi + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then echo 'Erroring on /conn depending on /host:'; ! go list -f '{{.ImportPath}} depends on {{join .Imports ", "}}' periph.io/x/periph/conn/... | sort | uniq | grep periph.io/x/periph/host; fi + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then bash -c 'set -e; echo "" > coverage.txt; for d in $(go list ./...); do go test -covermode=count -coverprofile=p.out $d; if [ -f p.out ]; then cat p.out >> coverage.txt; rm p.out; fi; done'; fi + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then go test -race ./...; fi + # The only thing run on older versions. + - if [[ $TRAVIS_GO_VERSION != 1.11.1 ]]; then go test ./...; fi +after_success: + - if [[ $TRAVIS_GO_VERSION == 1.11.1 ]]; then bash <(curl -s https://codecov.io/bash); fi diff --git a/vendor/periph.io/x/periph/AUTHORS b/vendor/periph.io/x/periph/AUTHORS new file mode 100644 index 0000000..e8ff861 --- /dev/null +++ b/vendor/periph.io/x/periph/AUTHORS @@ -0,0 +1,15 @@ +# This is the list of The Periph Authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +Cássio Botaro +Fractal Industries, Inc +Google Inc. +Josh Gardiner +Matt Aimonetti +Max Ekman +Rifiniti, Inc +Stephan Sperber +Thorsten von Eicken + diff --git a/vendor/periph.io/x/periph/CONTRIBUTING.md b/vendor/periph.io/x/periph/CONTRIBUTING.md new file mode 100644 index 0000000..56a90a0 --- /dev/null +++ b/vendor/periph.io/x/periph/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# Contributing + +Thanks for contributing to the project! Please look at [the periph contribution +guidelines](https://periph.io/project/contributing/) first. diff --git a/vendor/periph.io/x/periph/CONTRIBUTORS b/vendor/periph.io/x/periph/CONTRIBUTORS new file mode 100644 index 0000000..abd45a2 --- /dev/null +++ b/vendor/periph.io/x/periph/CONTRIBUTORS @@ -0,0 +1,40 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the periph repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# https://cla.developers.google.com/ +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Individual's name +# Individual's name +# +# An entry with multiple email addresses specifies that the +# first address should be used in the submit logs and +# that the other addresses should be recognized as the +# same person when interacting with Gerrit. + +# Please keep the list sorted. + +Cássio Botaro +Eugene Dzhurynsky +Hidetoshi Shimokawa +Josh Gardiner +Marc-Antoine Ruel +Matt Aimonetti +Max Ekman +Matias Insaurralde +Seán C McCord +Stephan Sperber +Thorsten von Eicken + diff --git a/vendor/periph.io/x/periph/LICENSE b/vendor/periph.io/x/periph/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/periph.io/x/periph/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/periph.io/x/periph/Makefile b/vendor/periph.io/x/periph/Makefile new file mode 100644 index 0000000..53b7fb4 --- /dev/null +++ b/vendor/periph.io/x/periph/Makefile @@ -0,0 +1,59 @@ +# Copyright 2016 The Periph Authors. All rights reserved. +# Use of this source code is governed under the Apache License, Version 2.0 +# that can be found in the LICENSE file. + +# This Makefile captures common tasks for the periph library. The hope is that this Makefile can remain +# simple and straightforward... + +# *** This Makefile is a work in progress, please help impove it! *** + +# not sure yet what all should do... +all: + @echo Available targets: test build + +.PHONY: all test clean depend + +# test runs the platform independent tests +# (gofmt|grep is used to obtain a non-zero exit status if the formatting is off) +test: + go test ./... + @if gofmt -l . | grep .go; then \ + echo "Repo contains improperly formatted go files; run gofmt on above files" && exit 1; \ + else echo "OK gofmt"; fi + -go vet -unsafeptr=false ./... + +# BUILD +# +# The build target cross compiles each program in cmd to a binary for each platform in the bin +# directory. It is assumed that each command has a main.go file in its directory. Trying to keep all +# this relatively simple and not descend into makefile hell... + +# Get a list of all main.go in cmd subdirs +# MAINS becomes: cmd/gpio-list/main.go cmd/periph-info/main.go ... +MAINS := $(wildcard cmd/*/main.go) +# Get a list of all the commands, i.e. names of dirs that contain a main.go +# CMDS becomes: gpio-list periph-info ... +CMDS := $(patsubst cmd/%/main.go,%,$(MAINS)) +# Get a list of binaries to build +# BINS becomes: bin/gpio-list-arm bin/periph-info-arm ... bin/gpio-list-arm64 bin/periph-info-arm64 ... +ARCHS := arm arm64 amd64 win64.exe +BINS=$(foreach arch,$(ARCHS),$(foreach cmd,$(CMDS),bin/$(cmd)-$(arch))) + +build: depend bin $(BINS) +bin: + mkdir bin + +# Rules to build binaries for a command in cmd. The prereqs could be improved... +bin/%-arm: cmd/%/*.go + GOARCH=arm GOOS=linux go build -o $@ ./cmd/$* +bin/%-arm64: cmd/%/*.go + GOARCH=arm64 GOOS=linux go build -o $@ ./cmd/$* +bin/%-amd64: cmd/%/*.go + GOARCH=amd64 GOOS=linux go build -o $@ ./cmd/$* +bin/%-win64.exe: cmd/%/*.go + GOARCH=amd64 GOOS=windows go build -o $@ ./cmd/$* + +# clean removes all compiled binaries +clean: + rm bin/*-* + rmdir bin diff --git a/vendor/periph.io/x/periph/README.md b/vendor/periph.io/x/periph/README.md new file mode 100644 index 0000000..dcb3ed1 --- /dev/null +++ b/vendor/periph.io/x/periph/README.md @@ -0,0 +1,59 @@ +# periph - Peripherals I/O in Go + +[![mascot](https://raw.githubusercontent.com/periph/website/master/site/static/img/periph-mascot-280.png)](https://periph.io/) + +Documentation is at https://periph.io + +[![GoDoc](https://godoc.org/periph.io/x/periph?status.svg)](https://godoc.org/periph.io/x/periph) +[![Go Report Card](https://goreportcard.com/badge/periph.io/x/periph)](https://goreportcard.com/report/periph.io/x/periph) +[![Coverage Status](https://codecov.io/gh/google/periph/graph/badge.svg)](https://codecov.io/gh/google/periph) +[![Build Status](https://travis-ci.org/google/periph.svg)](https://travis-ci.org/google/periph) + + +Join us for a chat on +[gophers.slack.com/messages/periph](https://gophers.slack.com/messages/periph), +get an [invite here](https://invite.slack.golangbridge.org/). + + +## Example + +Blink a LED: + +~~~go +package main + +import ( + "time" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/host" + "periph.io/x/periph/host/rpi" +) + +func main() { + host.Init() + t := time.NewTicker(500 * time.Millisecond) + for l := gpio.Low; ; l = !l { + rpi.P1_33.Out(l) + <-t.C + } +} +~~~ + +Curious? Look at [supported devices](https://periph.io/device/) for more +examples! + + +## Authors + +`periph` was initiated with ❤️️ and passion by [Marc-Antoine +Ruel](https://github.com/maruel). The full list of contributors is in +[AUTHORS](https://github.com/google/periph/blob/master/AUTHORS) and +[CONTRIBUTORS](https://github.com/google/periph/blob/master/CONTRIBUTORS). + + +## Disclaimer + +This is not an official Google product (experimental or otherwise), it +is just code that happens to be owned by Google. + +This project is not affiliated with the Go project. diff --git a/vendor/periph.io/x/periph/conn/conn.go b/vendor/periph.io/x/periph/conn/conn.go new file mode 100644 index 0000000..68e4f7a --- /dev/null +++ b/vendor/periph.io/x/periph/conn/conn.go @@ -0,0 +1,103 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package conn + +import "strconv" + +// Resource is a basic resource (like a gpio pin) or a device. +type Resource interface { + // String returns a human readable identifier representing this resource in a + // descriptive way for the user. It is the same signature as fmt.Stringer. + String() string + // Halt stops the resource. + // + // Unlike a Conn, a Resource may not be closable, On the other hand, a + // resource can be halted. What halting entails depends on the resource + // device but it should stop motion, sensing loop, light emission or PWM + // output and go back into an inert state. + Halt() error +} + +// Duplex declares whether communication can happen simultaneously both ways. +// +// Some protocol can be either depending on configuration settings, like UART. +type Duplex int + +const ( + // DuplexUnknown is used when the duplex of a connection is yet to be known. + // + // Some protocol can be configured either as half-duplex or full-duplex and + // the connection is not yet is a determinate state. + DuplexUnknown Duplex = 0 + // Half means that communication can only occurs one way at a time. + // + // Examples include 1-wire and I²C. + Half Duplex = 1 + // Full means that communication occurs simultaneously both ways in a + // synchronized manner. + // + // Examples include SPI (except 3-wire variant). + Full Duplex = 2 +) + +const duplexName = "DuplexUnknownHalfFull" + +var duplexIndex = [...]uint8{0, 13, 17, 21} + +func (i Duplex) String() string { + if i < 0 || i >= Duplex(len(duplexIndex)-1) { + return "Duplex(" + strconv.Itoa(int(i)) + ")" + } + return duplexName[duplexIndex[i]:duplexIndex[i+1]] +} + +// Conn defines the interface for a connection on a point-to-point +// communication channel. +// +// The connection can either be unidirectional (read-only, write-only) or +// bidirectional (read-write). It can either be half-duplex or full duplex. +// +// This is the lowest common denominator for all point-to-point communication +// channels. +// +// Implementation are expected but not required to also implement the following +// interfaces: +// +// - fmt.Stringer which returns something meaningful to the user like "SPI0.1", +// "I2C1.76", "COM6", etc. +// +// - io.Reader and io.Writer as a way to use io.Copy() for half duplex +// operation. +// +// - io.Closer for the owner of the communication channel. +type Conn interface { + String() string + // Tx does a single transaction. + // + // For full duplex protocols (generally SPI, UART), the two buffers must have + // the same length as both reading and writing happen simultaneously. + // + // For half duplex protocols (I²C), there is no restriction as reading + // happens after writing, and r can be nil. + // + // Query Limits.MaxTxSize() to know if there is a limit on the buffer size + // per Tx() call. + Tx(w, r []byte) error + // Duplex returns the current duplex setting for this point-to-point + // connection. + // + // It is expected to be either Half or Full unless the connection itself is + // in an unknown state. + Duplex() Duplex +} + +// Limits returns information about the connection's limits. +type Limits interface { + // MaxTxSize returns the maximum allowed data size to be sent as a single + // I/O. + // + // Returns 0 if undefined. + MaxTxSize() int +} diff --git a/vendor/periph.io/x/periph/conn/doc.go b/vendor/periph.io/x/periph/conn/doc.go new file mode 100644 index 0000000..2af0df5 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/doc.go @@ -0,0 +1,63 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package conn defines core interfaces for protocols and connections. +// +// This package and its subpackages describe the base interfaces to connect the +// software with the real world. It doesn't contain any implementation but +// includes registries to enable the application to discover the available +// hardware. +// +// Concepts +// +// periph uses 3 layered concepts for interfacing: +// +// Bus → Port → Conn +// +// Not every subpackage expose all 3 concepts. In fact, most packages don't. +// For example, SPI doesn't expose Bus as the OSes generally only expose the +// Port, that is, a Chip Select (CS) line must be selected right upfront to get +// an handle. For I²C, there's no Port to configure, so selecting a "slave" +// address is sufficient to jump directly from a Bus to a Conn. +// +// periph doesn't have yet a concept of star-like communication network, like +// an IP network. +// +// Bus +// +// A Bus is a multi-point communication channel where one "master" and multiple +// "slaves" communicate together. In the case of periph, the Bus handle is +// assumed to be the "master". The "master" generally initiates communications +// and selects the "slave" to talk to. +// +// As the "master" selects a "slave" over a bus, a virtual Port is +// automatically created. +// +// Examples include SPI, I²C and 1-wire. In each case, selecting a +// communication line (Chip Select (CS) line for SPI, address for I²C or +// 1-wire) converts the Bus into a Port. +// +// Port +// +// A port is a point-to-point communication channel that is yet to be +// initialized. It cannot be used for communication until it is connected and +// transformed into a Conn. Configuring a Port converts it into a Conn. Not all +// Port need configuration. +// +// Conn +// +// A Conn is a fully configured half or full duplex communication channel that +// is point-to-point, only between two devices. It is ready to use like any +// readable and/or writable pipe. +// +// Subpackages +// +// Most connection-type specific subpackages include subpackages: +// +// → XXXreg: registry as that is populated by the host drivers and that can be +// leveraged by applications. +// +// → XXXtest: fake implementation that can be leveraged when writing device +// driver unit test. +package conn diff --git a/vendor/periph.io/x/periph/conn/duplex_string.go b/vendor/periph.io/x/periph/conn/duplex_string.go new file mode 100644 index 0000000..9736bef --- /dev/null +++ b/vendor/periph.io/x/periph/conn/duplex_string.go @@ -0,0 +1,3 @@ +// Code generated by "stringer -type Duplex"; DO NOT EDIT + +package conn diff --git a/vendor/periph.io/x/periph/conn/gpio/func.go b/vendor/periph.io/x/periph/conn/gpio/func.go new file mode 100644 index 0000000..7bdb72b --- /dev/null +++ b/vendor/periph.io/x/periph/conn/gpio/func.go @@ -0,0 +1,25 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package gpio + +import "periph.io/x/periph/conn/pin" + +const ( + // Inputs + IN pin.Func = "IN" // Input + IN_HIGH pin.Func = "In/High" // Read high + IN_LOW pin.Func = "In/Low" // Read low + + // Outputs + OUT pin.Func = "OUT" // Output, drive + OUT_OC pin.Func = "OUT_OPEN" // Output, open collector/drain + OUT_HIGH pin.Func = "Out/High" // Drive high + OUT_LOW pin.Func = "Out/Low" // Drive low; open collector low + + FLOAT pin.Func = "FLOAT" // Input float or Output open collector high + + CLK pin.Func = "CLK" // Clock is a subset of a PWM, with a 50% duty cycle + PWM pin.Func = "PWM" // Pulse Width Modulation, which is a clock with variable duty cycle +) diff --git a/vendor/periph.io/x/periph/conn/gpio/gpio.go b/vendor/periph.io/x/periph/conn/gpio/gpio.go new file mode 100644 index 0000000..2f6112f --- /dev/null +++ b/vendor/periph.io/x/periph/conn/gpio/gpio.go @@ -0,0 +1,329 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package gpio defines digital pins. +// +// All GPIO implementations are expected to implement PinIO but the device +// driver may accept a more specific one like PinIn or PinOut. +package gpio + +import ( + "errors" + "strconv" + "strings" + "time" + + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/pin" +) + +// Interfaces + +// Level is the level of the pin: Low or High. +type Level bool + +const ( + // Low represents 0v. + Low Level = false + // High represents Vin, generally 3.3v or 5v. + High Level = true +) + +func (l Level) String() string { + if l == Low { + return "Low" + } + return "High" +} + +// Pull specifies the internal pull-up or pull-down for a pin set as input. +type Pull uint8 + +// Acceptable pull values. +const ( + PullNoChange Pull = 0 // Do not change the previous pull resistor setting or an unknown value + Float Pull = 1 // Let the input float + PullDown Pull = 2 // Apply pull-down + PullUp Pull = 3 // Apply pull-up +) + +const pullName = "PullNoChangeFloatPullDownPullUp" + +var pullIndex = [...]uint8{0, 12, 17, 25, 31} + +func (i Pull) String() string { + if i >= Pull(len(pullIndex)-1) { + return "Pull(" + strconv.Itoa(int(i)) + ")" + } + return pullName[pullIndex[i]:pullIndex[i+1]] +} + +// Edge specifies if an input pin should have edge detection enabled. +// +// Only enable it when needed, since this causes system interrupts. +type Edge int + +// Acceptable edge detection values. +const ( + NoEdge Edge = 0 + RisingEdge Edge = 1 + FallingEdge Edge = 2 + BothEdges Edge = 3 +) + +const edgeName = "NoEdgeRisingEdgeFallingEdgeBothEdges" + +var edgeIndex = [...]uint8{0, 6, 16, 27, 36} + +func (i Edge) String() string { + if i >= Edge(len(edgeIndex)-1) { + return "Edge(" + strconv.Itoa(int(i)) + ")" + } + return edgeName[edgeIndex[i]:edgeIndex[i+1]] +} + +const ( + // DutyMax is a duty cycle of 100%. + DutyMax Duty = 1 << 24 + // DutyHalf is a 50% duty PWM, which boils down to a normal clock. + DutyHalf Duty = DutyMax / 2 +) + +// Duty is the duty cycle for a PWM. +// +// Valid values are between 0 and DutyMax. +type Duty int32 + +func (d Duty) String() string { + // TODO(maruel): Implement one fractional number. + return strconv.Itoa(int((d+50)/(DutyMax/100))) + "%" +} + +// Valid returns true if the Duty cycle value is valid. +func (d Duty) Valid() bool { + return d >= 0 && d <= DutyMax +} + +// ParseDuty parses a string and converts it to a Duty value. +func ParseDuty(s string) (Duty, error) { + percent := strings.HasSuffix(s, "%") + if percent { + s = s[:len(s)-1] + } + i64, err := strconv.ParseInt(s, 10, 32) + if err != nil { + return 0, err + } + i := Duty(i64) + if percent { + // TODO(maruel): Add support for fractional number. + if i < 0 { + return 0, errors.New("duty must be >= 0%") + } + if i > 100 { + return 0, errors.New("duty must be <= 100%") + } + return ((i * DutyMax) + 49) / 100, nil + } + if i < 0 { + return 0, errors.New("duty must be >= 0") + } + if i > DutyMax { + return 0, errors.New("duty must be <= " + strconv.Itoa(int(DutyMax))) + } + return i, nil +} + +// PinIn is an input GPIO pin. +// +// It may optionally support internal pull resistor and edge based triggering. +// +// A button is semantically a PinIn. So if you are looking to read from a +// button, PinIn is the interface you are looking for. +type PinIn interface { + pin.Pin + // In setups a pin as an input. + // + // If WaitForEdge() is planned to be called, make sure to use one of the Edge + // value. Otherwise, use NoEdge to not generated unneeded hardware interrupts. + // + // Calling In() will try to empty the accumulated edges but it cannot be 100% + // reliable due to the OS (linux) and its driver. It is possible that on a + // gpio that is as input, doing a quick Out(), In() may return an edge that + // occurred before the Out() call. + In(pull Pull, edge Edge) error + // Read return the current pin level. + // + // Behavior is undefined if In() wasn't used before. + // + // In some rare case, it is possible that Read() fails silently. This happens + // if another process on the host messes up with the pin after In() was + // called. In this case, call In() again. + Read() Level + // WaitForEdge() waits for the next edge or immediately return if an edge + // occurred since the last call. + // + // Only waits for the kind of edge as specified in a previous In() call. + // Behavior is undefined if In() with a value other than NoEdge wasn't called + // before. + // + // Returns true if an edge was detected during or before this call. Return + // false if the timeout occurred or In() was called while waiting, causing the + // function to exit. + // + // Multiple edges may or may not accumulate between two calls to + // WaitForEdge(). The behavior in this case is undefined and is OS driver + // specific. + // + // It is not required to call Read() to reset the edge detection. + // + // Specify -1 to effectively disable timeout. + WaitForEdge(timeout time.Duration) bool + // Pull returns the internal pull resistor if the pin is set as input pin. + // + // Returns PullNoChange if the value cannot be read. + Pull() Pull + // DefaultPull returns the pull that is initialized on CPU/device reset. This + // is useful to determine if the pin is acceptable for operation with + // certain devices. + DefaultPull() Pull +} + +// PinOut is an output GPIO pin. +// +// A LED, a buzzer, a servo, are semantically a PinOut. So if you are looking +// to control these, PinOut is the interface you are looking for. +type PinOut interface { + pin.Pin + // Out sets a pin as output if it wasn't already and sets the initial value. + // + // After the initial call to ensure that the pin has been set as output, it + // is generally safe to ignore the error returned. + // + // Out() tries to empty the accumulated edges detected if the gpio was + // previously set as input but this is not 100% guaranteed due to the OS. + Out(l Level) error + // PWM sets the PWM output on supported pins, if the pin has hardware PWM + // support. + // + // To use as a general purpose clock, set duty to DutyHalf. Some pins may + // only support DutyHalf and no other value. + // + // Using 0 as frequency will use the optimal value as supported/preferred by + // the pin. + // + // To use as a servo, see https://en.wikipedia.org/wiki/Servo_control as an + // explanation how to calculate duty. + PWM(duty Duty, f physic.Frequency) error +} + +// PinIO is a GPIO pin that supports both input and output. It matches both +// interfaces PinIn and PinOut. +// +// A GPIO pin implementing PinIO may fail at either input or output or both. +type PinIO interface { + pin.Pin + // PinIn + In(pull Pull, edge Edge) error + Read() Level + WaitForEdge(timeout time.Duration) bool + Pull() Pull + DefaultPull() Pull + // PinOut + Out(l Level) error + PWM(duty Duty, f physic.Frequency) error +} + +// INVALID implements PinIO and fails on all access. +var INVALID PinIO + +// RealPin is implemented by aliased pin and allows the retrieval of the real +// pin underlying an alias. +// +// Aliases are created by RegisterAlias. Aliases permits presenting a user +// friendly GPIO pin name while representing the underlying real pin. +// +// The purpose of the RealPin is to be able to cleanly test whether an arbitrary +// gpio.PinIO returned by ByName is an alias for another pin, and resolve it. +type RealPin interface { + Real() PinIO // Real returns the real pin behind an Alias +} + +// + +// errInvalidPin is returned when trying to use INVALID. +var errInvalidPin = errors.New("gpio: invalid pin") + +func init() { + INVALID = invalidPin{} +} + +// invalidPin implements PinIO for compatibility but fails on all access. +type invalidPin struct { +} + +func (invalidPin) String() string { + return "INVALID" +} + +func (invalidPin) Halt() error { + return nil +} + +func (invalidPin) Number() int { + return -1 +} + +func (invalidPin) Name() string { + return "INVALID" +} + +func (invalidPin) Function() string { + return "" +} + +func (invalidPin) Func() pin.Func { + return pin.FuncNone +} + +func (invalidPin) SupportedFuncs() []pin.Func { + return nil +} + +func (invalidPin) SetFunc(f pin.Func) error { + return errInvalidPin +} + +func (invalidPin) In(Pull, Edge) error { + return errInvalidPin +} + +func (invalidPin) Read() Level { + return Low +} + +func (invalidPin) WaitForEdge(timeout time.Duration) bool { + return false +} + +func (invalidPin) Pull() Pull { + return PullNoChange +} + +func (invalidPin) DefaultPull() Pull { + return PullNoChange +} + +func (invalidPin) Out(Level) error { + return errInvalidPin +} + +func (invalidPin) PWM(Duty, physic.Frequency) error { + return errInvalidPin +} + +var _ PinIn = INVALID +var _ PinOut = INVALID +var _ PinIO = INVALID +var _ pin.PinFunc = &invalidPin{} diff --git a/vendor/periph.io/x/periph/conn/gpio/gpioreg/gpioreg.go b/vendor/periph.io/x/periph/conn/gpio/gpioreg/gpioreg.go new file mode 100644 index 0000000..65f2de5 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/gpio/gpioreg/gpioreg.go @@ -0,0 +1,213 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package gpioreg defines a registry for the known digital pins. +package gpioreg + +import ( + "errors" + "strconv" + "sync" + + "periph.io/x/periph/conn/gpio" +) + +// ByName returns a GPIO pin from its name, gpio number or one of its aliases. +// +// For example on a Raspberry Pi, the following values will return the same +// GPIO: the gpio as a number "2", the chipset name "GPIO2", the board pin +// position "P1_3", it's function name "I2C1_SDA". +// +// Returns nil if the gpio pin is not present. +func ByName(name string) gpio.PinIO { + mu.Lock() + defer mu.Unlock() + if p, ok := byName[name]; ok { + return p + } + if dest, ok := byAlias[name]; ok { + if p := getByNameDeep(dest); p != nil { + // Wraps the destination in an alias, so the name makes sense to the user. + // The main drawback is that casting into other gpio interfaces like + // gpio.PinPWM requires going through gpio.RealPin first. + return &pinAlias{p, name} + } + } + return nil +} + +// All returns all the GPIO pins available on this host. +// +// The list is guaranteed to be in order of name using 'natural sorting'. +// +// This list excludes aliases. +// +// This list excludes non-GPIO pins like GROUND, V3_3, etc, since they are not +// GPIO. +func All() []gpio.PinIO { + mu.Lock() + defer mu.Unlock() + out := make([]gpio.PinIO, 0, len(byName)) + for _, p := range byName { + out = insertPinByName(out, p) + } + return out +} + +// Aliases returns all pin aliases. +// +// The list is guaranteed to be in order of aliase name. +func Aliases() []gpio.PinIO { + mu.Lock() + defer mu.Unlock() + out := make([]gpio.PinIO, 0, len(byAlias)) + for name, dest := range byAlias { + // Skip aliases that were not resolved. + if p := getByNameDeep(dest); p != nil { + out = insertPinByName(out, &pinAlias{p, name}) + } + } + return out +} + +// Register registers a GPIO pin. +// +// Registering the same pin number or name twice is an error. +// +// The pin registered cannot implement the interface RealPin. +func Register(p gpio.PinIO) error { + name := p.Name() + if len(name) == 0 { + return errors.New("gpioreg: can't register a pin with no name") + } + if r, ok := p.(gpio.RealPin); ok { + return errors.New("gpioreg: can't register pin " + strconv.Quote(name) + ", it is already an alias to " + strconv.Quote(r.Real().String())) + } + + mu.Lock() + defer mu.Unlock() + if orig, ok := byName[name]; ok { + return errors.New("gpioreg: can't register pin " + strconv.Quote(name) + " twice; already registered as " + strconv.Quote(orig.String())) + } + if dest, ok := byAlias[name]; ok { + return errors.New("gpioreg: can't register pin " + strconv.Quote(name) + "; an alias already exist to: " + strconv.Quote(dest)) + } + byName[name] = p + return nil +} + +// RegisterAlias registers an alias for a GPIO pin. +// +// It is possible to register an alias for a pin that itself has not been +// registered yet. It is valid to register an alias to another alias. It is +// valid to register the same alias multiple times, overriding the previous +// alias. +func RegisterAlias(alias string, dest string) error { + if len(alias) == 0 { + return errors.New("gpioreg: can't register an alias with no name") + } + if len(dest) == 0 { + return errors.New("gpioreg: can't register alias " + strconv.Quote(alias) + " with no dest") + } + + mu.Lock() + defer mu.Unlock() + if _, ok := byName[alias]; ok { + return errors.New("gpioreg: can't register alias " + strconv.Quote(alias) + " for a pin that exists") + } + byAlias[alias] = dest + return nil +} + +// Unregister removes a previously registered GPIO pin or alias from the GPIO +// pin registry. +// +// This can happen when a GPIO pin is exposed via an USB device and the device +// is unplugged, or when a generic OS provided pin is superseded by a CPU +// specific implementation. +func Unregister(name string) error { + mu.Lock() + defer mu.Unlock() + if _, ok := byName[name]; ok { + delete(byName, name) + return nil + } + if _, ok := byAlias[name]; ok { + delete(byAlias, name) + return nil + } + return errors.New("gpioreg: can't unregister unknown pin name " + strconv.Quote(name)) +} + +// + +var ( + mu sync.Mutex + byName = map[string]gpio.PinIO{} + byAlias = map[string]string{} +) + +// pinAlias implements an alias for a PinIO. +// +// pinAlias implements the RealPin interface, which allows querying for the +// real pin under the alias. +type pinAlias struct { + gpio.PinIO + name string +} + +// String returns the alias name along the real pin's Name() in parenthesis, if +// known, else the real pin's number. +func (a *pinAlias) String() string { + return a.name + "(" + a.PinIO.Name() + ")" +} + +// Name returns the pinAlias's name. +func (a *pinAlias) Name() string { + return a.name +} + +// Real returns the real pin behind the alias +func (a *pinAlias) Real() gpio.PinIO { + return a.PinIO +} + +// getByNameDeep recursively resolves the aliases to get the pin. +func getByNameDeep(name string) gpio.PinIO { + if p, ok := byName[name]; ok { + return p + } + if dest, ok := byAlias[name]; ok { + if p := getByNameDeep(dest); p != nil { + // Return the deep pin directly, bypassing the aliases. + return p + } + } + return nil +} + +// insertPinByName inserts pin p into list l while keeping l ordered by name. +func insertPinByName(l []gpio.PinIO, p gpio.PinIO) []gpio.PinIO { + n := p.Name() + i := search(len(l), func(i int) bool { return lessNatural(n, l[i].Name()) }) + l = append(l, nil) + copy(l[i+1:], l[i:]) + l[i] = p + return l +} + +// search implements the same algorithm as sort.Search(). +// +// It was extracted to to not depend on sort, which depends on reflect. +func search(n int, f func(int) bool) int { + lo := 0 + for hi := n; lo < hi; { + if i := int(uint(lo+hi) >> 1); !f(i) { + lo = i + 1 + } else { + hi = i + } + } + return lo +} diff --git a/vendor/periph.io/x/periph/conn/gpio/gpioreg/natsort.go b/vendor/periph.io/x/periph/conn/gpio/gpioreg/natsort.go new file mode 100644 index 0000000..d8d1a76 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/gpio/gpioreg/natsort.go @@ -0,0 +1,76 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package gpioreg + +import ( + "strconv" +) + +// lessNatural does a 'natural' comparison on the two strings. +// +// It is extracted from https://github.com/maruel/natural. +func lessNatural(a, b string) bool { + for { + if a == b { + return false + } + if p := commonPrefix(a, b); p != 0 { + a = a[p:] + b = b[p:] + } + if ia := digits(a); ia > 0 { + if ib := digits(b); ib > 0 { + // Both sides have digits. + an, aerr := strconv.ParseUint(a[:ia], 10, 64) + bn, berr := strconv.ParseUint(b[:ib], 10, 64) + if aerr == nil && berr == nil { + if an != bn { + return an < bn + } + // Semantically the same digits, e.g. "00" == "0", "01" == "1". In + // this case, only continue processing if there's trailing data on + // both sides, otherwise do lexical comparison. + if ia != len(a) && ib != len(b) { + a = a[ia:] + b = b[ib:] + continue + } + } + } + } + return a < b + } +} + +// commonPrefix returns the common prefix except for digits. +func commonPrefix(a, b string) int { + m := len(a) + if n := len(b); n < m { + m = n + } + if m == 0 { + return 0 + } + _ = a[m-1] + _ = b[m-1] + for i := 0; i < m; i++ { + ca := a[i] + cb := b[i] + if (ca >= '0' && ca <= '9') || (cb >= '0' && cb <= '9') || ca != cb { + return i + } + } + return m +} + +func digits(s string) int { + for i := 0; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + return i + } + } + return len(s) +} diff --git a/vendor/periph.io/x/periph/conn/gpio/gpiostream/gpiostream.go b/vendor/periph.io/x/periph/conn/gpio/gpiostream/gpiostream.go new file mode 100644 index 0000000..bf80cb9 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/gpio/gpiostream/gpiostream.go @@ -0,0 +1,217 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package gpiostream defines digital streams. +// +// Warning +// +// This package is still in flux as development is on-going. +package gpiostream + +import ( + "fmt" + "time" + + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/physic" +) + +// Stream is the interface to define a generic stream. +type Stream interface { + // Frequency is the minimum data rate at which the binary stream is usable. + // + // For example, a bit stream may have a 10kHz data rate. + Frequency() physic.Frequency + // Duration of the binary stream. For infinitely looping streams, it is the + // duration of the non-looping part. + Duration() time.Duration +} + +// BitStream is a stream of bits to be written or read. +type BitStream struct { + // Bits is a densely packed bitstream. + // + // The stream is required to be a multiple of 8 samples. + Bits []byte + // Freq is the rate at each the bit (not byte) stream should be processed. + Freq physic.Frequency + // LSBF when true means than Bits is in LSB-first. When false, the data is + // MSB-first. + // + // With MSBF, the first bit processed is the most significant one (0x80). For + // example, I²C, I2S PCM and SPI use MSB-first at the word level. This + // requires to pack words correctly. + // + // With LSBF, the first bit processed is the least significant one (0x01). + // For example, Ethernet uses LSB-first at the byte level and MSB-first at + // the word level. + LSBF bool +} + +// Frequency implements Stream. +func (b *BitStream) Frequency() physic.Frequency { + return b.Freq +} + +// Duration implements Stream. +func (b *BitStream) Duration() time.Duration { + if b.Freq == 0 { + return 0 + } + return b.Freq.Duration() * time.Duration(len(b.Bits)*8) +} + +// GoString implements fmt.GoStringer. +func (b *BitStream) GoString() string { + return fmt.Sprintf("&gpiostream.BitStream{Bits: %x, Freq:%s, LSBF:%t}", b.Bits, b.Freq, b.LSBF) +} + +// EdgeStream is a stream of edges to be written. +// +// This struct is more efficient than BitStream for short repetitive pulses, +// like controlling a servo. A PWM can be created by specifying a slice of +// twice the same resolution and make it looping via a Program. +type EdgeStream struct { + // Edges is the list of Level change. It is assumed that the signal starts + // with gpio.High. Use a duration of 0 for Edges[0] to start with a Low + // instead of the default High. + // + // The value is a multiple of Res. Use a 0 value to 'extend' a continuous + // signal that lasts more than "2^16-1*Res" duration by skipping a pulse. + Edges []uint16 + // Res is the minimum resolution at which the edges should be + // rasterized. + // + // The lower the value, the more memory shall be used when rasterized. + Freq physic.Frequency +} + +// Frequency implements Stream. +func (e *EdgeStream) Frequency() physic.Frequency { + return e.Freq +} + +// Duration implements Stream. +func (e *EdgeStream) Duration() time.Duration { + if e.Freq == 0 { + return 0 + } + t := 0 + for _, edge := range e.Edges { + t += int(edge) + } + return e.Freq.Duration() * time.Duration(t) +} + +// Program is a loop of streams. +// +// This is itself a stream, it can be used to reduce memory usage when repeated +// patterns are used. +type Program struct { + Parts []Stream // Each part must be a BitStream, EdgeStream or Program + Loops int // Set to -1 to create an infinite loop +} + +// Frequency implements Stream. +func (p *Program) Frequency() physic.Frequency { + if p.Loops == 0 { + return 0 + } + var buf [16]physic.Frequency + freqs := buf[:0] + for _, part := range p.Parts { + if f := part.Frequency(); f != 0 { + freqs = insertFreq(freqs, f) + } + } + if len(freqs) == 0 { + return 0 + } + f := freqs[0] + for i := 1; i < len(freqs); i++ { + if r := freqs[i]; r*2 < f { + break + } + // Take in account Nyquist rate. https://wikipedia.org/wiki/Nyquist_rate + f *= 2 + } + return f +} + +// Duration implements Stream. +func (p *Program) Duration() time.Duration { + if p.Loops == 0 { + return 0 + } + var d time.Duration + for _, s := range p.Parts { + d += s.Duration() + } + if p.Loops > 1 { + d *= time.Duration(p.Loops) + } + return d +} + +// + +// PinIn allows to read a bit stream from a pin. +// +// Caveat +// +// This interface doesn't enable sampling multiple pins in a +// synchronized way or reading in a continuous uninterrupted way. As such, it +// should be considered experimental. +type PinIn interface { + // StreamIn reads for the pin at the specified resolution to fill the + // provided buffer. + // + // May only support a subset of the structs implementing Stream. + StreamIn(p gpio.Pull, b Stream) error +} + +// PinOut allows to stream to a pin. +// +// The Stream may be a Program, a BitStream or an EdgeStream. If it is a +// Program that is an infinite loop, a separate goroutine can be used to cancel +// the program. In this case StreamOut() returns without an error. +// +// Caveat +// +// This interface doesn't enable streaming to multiple pins in a +// synchronized way or reading in a continuous uninterrupted way. As such, it +// should be considered experimental. +type PinOut interface { + StreamOut(s Stream) error +} + +// + +// insertFreq inserts in reverse order, highest frequency first. +func insertFreq(l []physic.Frequency, f physic.Frequency) []physic.Frequency { + i := search(len(l), func(i int) bool { return l[i] < f }) + l = append(l, 0) + copy(l[i+1:], l[i:]) + l[i] = f + return l +} + +// search implements the same algorithm as sort.Search(). +// +// It was extracted to to not depend on sort, which depends on reflect. +func search(n int, f func(int) bool) int { + lo := 0 + for hi := n; lo < hi; { + if i := int(uint(lo+hi) >> 1); !f(i) { + lo = i + 1 + } else { + hi = i + } + } + return lo +} + +var _ Stream = &BitStream{} +var _ Stream = &EdgeStream{} +var _ Stream = &Program{} diff --git a/vendor/periph.io/x/periph/conn/i2c/func.go b/vendor/periph.io/x/periph/conn/i2c/func.go new file mode 100644 index 0000000..12fe16b --- /dev/null +++ b/vendor/periph.io/x/periph/conn/i2c/func.go @@ -0,0 +1,12 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package i2c + +import "periph.io/x/periph/conn/pin" + +const ( + SCL pin.Func = "I2C_SCL" // Clock + SDA pin.Func = "I2C_SDA" // Data +) diff --git a/vendor/periph.io/x/periph/conn/i2c/i2c.go b/vendor/periph.io/x/periph/conn/i2c/i2c.go new file mode 100644 index 0000000..ed444f5 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/i2c/i2c.go @@ -0,0 +1,114 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package i2c defines the API to communicate with devices over the I²C +// protocol. +// +// As described in https://periph.io/x/periph/conn#hdr-Concepts, periph.io uses +// the concepts of Bus, Port and Conn. +// +// In the package i2c, 'Port' is not exposed, since once you know the I²C +// device address, there's no unconfigured Port to configure. +// +// Instead, the package includes the adapter 'Dev' to directly convert an I²C +// bus 'i2c.Bus' into a connection 'conn.Conn' by only specifying the device +// I²C address. +// +// See https://en.wikipedia.org/wiki/I%C2%B2C for more information. +package i2c + +import ( + "io" + "strconv" + + "periph.io/x/periph/conn" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/physic" +) + +// Bus defines the interface a concrete I²C driver must implement. +// +// This interface is consummed by a device driver for a device sitting on a bus. +// +// This interface doesn't implement conn.Conn since a device address must be +// specified. Use i2cdev.Dev as an adapter to get a conn.Conn compatible +// object. +type Bus interface { + String() string + // Tx does a transaction at the specified device address. + // + // Write is done first, then read. One of 'w' or 'r' can be omitted for a + // unidirectional operation. + Tx(addr uint16, w, r []byte) error + // SetSpeed changes the bus speed, if supported. + // + // On linux due to the way the I²C sysfs driver is exposed in userland, + // calling this function will likely affect *all* I²C buses on the host. + SetSpeed(f physic.Frequency) error +} + +// BusCloser is an I²C bus that can be closed. +// +// This interface is meant to be handled by the application and not the device +// driver. A device driver doesn't "own" a bus, hence it must operate on a Bus, +// not a BusCloser. +type BusCloser interface { + io.Closer + Bus +} + +// Pins defines the pins that an I²C bus interconnect is using on the host. +// +// It is expected that a implementer of Bus also implement Pins but this is not +// a requirement. +type Pins interface { + // SCL returns the CLK (clock) pin. + SCL() gpio.PinIO + // SDA returns the DATA pin. + SDA() gpio.PinIO +} + +// Dev is a device on a I²C bus. +// +// It implements conn.Conn. +// +// It saves from repeatedly specifying the device address. +type Dev struct { + Bus Bus + Addr uint16 +} + +func (d *Dev) String() string { + s := "" + if d.Bus != nil { + s = d.Bus.String() + } + return s + "(" + strconv.Itoa(int(d.Addr)) + ")" +} + +// Tx does a transaction by adding the device's address to each command. +// +// It's a wrapper for Bus.Tx(). +func (d *Dev) Tx(w, r []byte) error { + return d.Bus.Tx(d.Addr, w, r) +} + +// Write writes to the I²C bus without reading, implementing io.Writer. +// +// It's a wrapper for Tx() +func (d *Dev) Write(b []byte) (int, error) { + if err := d.Tx(b, nil); err != nil { + return 0, err + } + return len(b), nil +} + +// Duplex always return conn.Half for I²C. +func (d *Dev) Duplex() conn.Duplex { + return conn.Half +} + +// + +var _ conn.Conn = &Dev{} diff --git a/vendor/periph.io/x/periph/conn/i2c/i2creg/i2creg.go b/vendor/periph.io/x/periph/conn/i2c/i2creg/i2creg.go new file mode 100644 index 0000000..69c828b --- /dev/null +++ b/vendor/periph.io/x/periph/conn/i2c/i2creg/i2creg.go @@ -0,0 +1,252 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package i2creg defines I²C bus registry to list buses present on the host. +package i2creg + +import ( + "errors" + "strconv" + "strings" + "sync" + + "periph.io/x/periph/conn/i2c" +) + +// Opener opens an handle to a bus. +// +// It is provided by the actual bus driver. +type Opener func() (i2c.BusCloser, error) + +// Ref references an I²C bus. +// +// It is returned by All() to enumerate all registered buses. +type Ref struct { + // Name of the bus. + // + // It must not be a sole number. It must be unique across the host. + Name string + // Aliases are the alternative names that can be used to reference this bus. + Aliases []string + // Number of the bus or -1 if the bus doesn't have any "native" number. + // + // Buses provided by the CPU normally have a 0 based number. Buses provided + // via an addon (like over USB) generally are not numbered. + Number int + // Open is the factory to open an handle to this I²C bus. + Open Opener +} + +// Open opens an I²C bus by its name, an alias or its number and returns an +// handle to it. +// +// Specify the empty string "" to get the first available bus. This is the +// recommended default value unless an application knows the exact bus to use. +// +// Each bus can register multiple aliases, each leading to the same bus handle. +// +// "Bus number" is a generic concept that is highly dependent on the platform +// and OS. On some platform, the first bus may have the number 0, 1 or higher. +// Bus numbers are not necessarily continuous and may not start at 0. It was +// observed that the bus number as reported by the OS may change across OS +// revisions. +// +// When the I²C bus is provided by an off board plug and play bus like USB via +// a FT232H USB device, there can be no associated number. +func Open(name string) (i2c.BusCloser, error) { + var r *Ref + var err error + func() { + mu.Lock() + defer mu.Unlock() + if len(byName) == 0 { + err = errors.New("i2creg: no bus found; did you forget to call Init()?") + return + } + if len(name) == 0 { + r = getDefault() + return + } + // Try by name, by alias, by number. + if r = byName[name]; r == nil { + if r = byAlias[name]; r == nil { + if i, err2 := strconv.Atoi(name); err2 == nil { + r = byNumber[i] + } + } + } + }() + if err != nil { + return nil, err + } + if r == nil { + return nil, errors.New("i2creg: can't open unknown bus: " + strconv.Quote(name)) + } + return r.Open() +} + +// All returns a copy of all the registered references to all know I²C buses +// available on this host. +// +// The list is sorted by the bus name. +func All() []*Ref { + mu.Lock() + defer mu.Unlock() + out := make([]*Ref, 0, len(byName)) + for _, v := range byName { + r := &Ref{Name: v.Name, Aliases: make([]string, len(v.Aliases)), Number: v.Number, Open: v.Open} + copy(r.Aliases, v.Aliases) + out = insertRef(out, r) + } + return out +} + +// Register registers an I²C bus. +// +// Registering the same bus name twice is an error, e.g. o.Name(). o.Number() +// can be -1 to signify that the bus doesn't have an inherent "bus number". A +// good example is a bus provided over a FT232H device connected on an USB bus. +// In this case, the bus name should be created from the serial number of the +// device for unique identification. +func Register(name string, aliases []string, number int, o Opener) error { + if len(name) == 0 { + return errors.New("i2creg: can't register a bus with no name") + } + if o == nil { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with nil Opener") + } + if number < -1 { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with invalid bus number " + strconv.Itoa(number)) + } + if _, err := strconv.Atoi(name); err == nil { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with name being only a number") + } + if strings.Contains(name, ":") { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with name containing ':'") + } + for _, alias := range aliases { + if len(alias) == 0 { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an empty alias") + } + if name == alias { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias the same as the bus name") + } + if _, err := strconv.Atoi(alias); err == nil { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias that is a number: " + strconv.Quote(alias)) + } + if strings.Contains(alias, ":") { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias containing ':': " + strconv.Quote(alias)) + } + } + + mu.Lock() + defer mu.Unlock() + if _, ok := byName[name]; ok { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice") + } + if _, ok := byAlias[name]; ok { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; it is already an alias") + } + if number != -1 { + if _, ok := byNumber[number]; ok { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + "; bus number " + strconv.Itoa(number) + " is already registered") + } + } + for _, alias := range aliases { + if _, ok := byName[alias]; ok { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already a bus") + } + if _, ok := byAlias[alias]; ok { + return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already an alias") + } + } + + r := &Ref{Name: name, Aliases: make([]string, len(aliases)), Number: number, Open: o} + copy(r.Aliases, aliases) + byName[name] = r + if number != -1 { + byNumber[number] = r + } + for _, alias := range aliases { + byAlias[alias] = r + } + return nil +} + +// Unregister removes a previously registered I²C bus. +// +// This can happen when an I²C bus is exposed via an USB device and the device +// is unplugged. +func Unregister(name string) error { + mu.Lock() + defer mu.Unlock() + r := byName[name] + if r == nil { + return errors.New("i2creg: can't unregister unknown bus name " + strconv.Quote(name)) + } + delete(byName, name) + delete(byNumber, r.Number) + for _, alias := range r.Aliases { + delete(byAlias, alias) + } + return nil +} + +// + +var ( + mu sync.Mutex + byName = map[string]*Ref{} + // Caches + byNumber = map[int]*Ref{} + byAlias = map[string]*Ref{} +) + +// getDefault returns the Ref that should be used as the default bus. +func getDefault() *Ref { + var o *Ref + if len(byNumber) == 0 { + // Fallback to use byName using a lexical sort. + name := "" + for n, o2 := range byName { + if len(name) == 0 || n < name { + o = o2 + name = n + } + } + return o + } + number := int((^uint(0)) >> 1) + for n, o2 := range byNumber { + if number > n { + number = n + o = o2 + } + } + return o +} + +func insertRef(l []*Ref, r *Ref) []*Ref { + n := r.Name + i := search(len(l), func(i int) bool { return l[i].Name > n }) + l = append(l, nil) + copy(l[i+1:], l[i:]) + l[i] = r + return l +} + +// search implements the same algorithm as sort.Search(). +// +// It was extracted to to not depend on sort, which depends on reflect. +func search(n int, f func(int) bool) int { + lo := 0 + for hi := n; lo < hi; { + if i := int(uint(lo+hi) >> 1); !f(i) { + lo = i + 1 + } else { + hi = i + } + } + return lo +} diff --git a/vendor/periph.io/x/periph/conn/physic/doc.go b/vendor/periph.io/x/periph/conn/physic/doc.go new file mode 100644 index 0000000..d1af92b --- /dev/null +++ b/vendor/periph.io/x/periph/conn/physic/doc.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package physic declares types for physical input, outputs and measurement +// units. +// +// This includes temperature, humidity, pressure, tension, current, etc. +package physic diff --git a/vendor/periph.io/x/periph/conn/physic/physic.go b/vendor/periph.io/x/periph/conn/physic/physic.go new file mode 100644 index 0000000..550855f --- /dev/null +++ b/vendor/periph.io/x/periph/conn/physic/physic.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package physic + +import ( + "time" + + "periph.io/x/periph/conn" +) + +// Env represents measurements from an environmental sensor. +type Env struct { + Temperature Temperature + Pressure Pressure + Humidity RelativeHumidity +} + +// SenseEnv represents an environmental sensor. +type SenseEnv interface { + conn.Resource + + // Sense returns the value read from the sensor. Unsupported metrics are not + // modified. + Sense(env *Env) error + // SenseContinuous initiates a continuous sensing at the specified interval. + // + // It is important to call Halt() once done with the sensing, which will turn + // the device off and will close the channel. + SenseContinuous(interval time.Duration) (<-chan Env, error) + // Precision returns this sensor's precision. + // + // The env values are set to the number of bits that are significant for each + // items that this sensor can measure. + // + // Precision is not accuracy. The sensor may have absolute and relative + // errors in its measurement, that are likely well above the reported + // precision. Accuracy may be improved on some sensor by using oversampling, + // or doing oversampling in software. Refer to its datasheet if available. + Precision(env *Env) +} diff --git a/vendor/periph.io/x/periph/conn/physic/units.go b/vendor/periph.io/x/periph/conn/physic/units.go new file mode 100644 index 0000000..a03a8cf --- /dev/null +++ b/vendor/periph.io/x/periph/conn/physic/units.go @@ -0,0 +1,1043 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package physic + +import ( + "errors" + "strconv" + "time" +) + +// Angle is the measurement of the difference in orientation between two vectors +// stored as an int64 nano radian. +// +// A negative angle is valid. +// +// The highest representable value is a bit over 500,000,000,000°. +type Angle int64 + +// String returns the angle formatted as a string in degree. +func (a Angle) String() string { + // Angle is not a S.I. unit, so it must not be prefixed by S.I. prefixes. + if a == 0 { + return "0°" + } + // Round. + prefix := "" + if a < 0 { + a = -a + prefix = "-" + } + switch { + case a < Degree: + v := ((a * 1000) + Degree/2) / Degree + return prefix + "0." + prefixZeros(3, int(v)) + "°" + case a < 10*Degree: + v := ((a * 1000) + Degree/2) / Degree + i := v / 1000 + v = v - i*1000 + return prefix + strconv.FormatInt(int64(i), 10) + "." + prefixZeros(3, int(v)) + "°" + case a < 100*Degree: + v := ((a * 1000) + Degree/2) / Degree + i := v / 1000 + v = v - i*1000 + return prefix + strconv.FormatInt(int64(i), 10) + "." + prefixZeros(2, int(v)) + "°" + case a < 1000*Degree: + v := ((a * 1000) + Degree/2) / Degree + i := v / 1000 + v = v - i*1000 + return prefix + strconv.FormatInt(int64(i), 10) + "." + prefixZeros(1, int(v)) + "°" + default: + v := (a + Degree/2) / Degree + return prefix + strconv.FormatInt(int64(v), 10) + "°" + } +} + +const ( + NanoRadian Angle = 1 + MicroRadian Angle = 1000 * NanoRadian + MilliRadian Angle = 1000 * MicroRadian + Radian Angle = 1000 * MilliRadian + + // Theta is 2π. This is equivalent to 360°. + Theta Angle = 6283185307 * NanoRadian + Pi Angle = 3141592653 * NanoRadian + Degree Angle = 17453293 * NanoRadian +) + +// Distance is a measurement of length stored as an int64 nano metre. +// +// This is one of the base unit in the International System of Units. +// +// The highest representable value is 9.2Gm. +type Distance int64 + +// String returns the distance formatted as a string in metre. +func (d Distance) String() string { + return nanoAsString(int64(d)) + "m" +} + +const ( + NanoMetre Distance = 1 + MicroMetre Distance = 1000 * NanoMetre + MilliMetre Distance = 1000 * MicroMetre + Metre Distance = 1000 * MilliMetre + KiloMetre Distance = 1000 * Metre + MegaMetre Distance = 1000 * KiloMetre + GigaMetre Distance = 1000 * MegaMetre + + // Conversion between Metre and imperial units. + Thou Distance = 25400 * NanoMetre + Inch Distance = 1000 * Thou + Foot Distance = 12 * Inch + Yard Distance = 3 * Foot + Mile Distance = 1760 * Yard +) + +// ElectricCurrent is a measurement of a flow of electric charge stored as an +// int64 nano Ampere. +// +// This is one of the base unit in the International System of Units. +// +// The highest representable value is 9.2GA. +type ElectricCurrent int64 + +// String returns the current formatted as a string in Ampere. +func (e ElectricCurrent) String() string { + return nanoAsString(int64(e)) + "A" +} + +const ( + NanoAmpere ElectricCurrent = 1 + MicroAmpere ElectricCurrent = 1000 * NanoAmpere + MilliAmpere ElectricCurrent = 1000 * MicroAmpere + Ampere ElectricCurrent = 1000 * MilliAmpere + KiloAmpere ElectricCurrent = 1000 * Ampere + MegaAmpere ElectricCurrent = 1000 * KiloAmpere + GigaAmpere ElectricCurrent = 1000 * MegaAmpere +) + +// ElectricPotential is a measurement of electric potential stored as an int64 +// nano Volt. +// +// The highest representable value is 9.2GV. +type ElectricPotential int64 + +// String returns the tension formatted as a string in Volt. +func (e ElectricPotential) String() string { + return nanoAsString(int64(e)) + "V" +} + +const ( + // Volt is W/A, kg⋅m²/s³/A. + NanoVolt ElectricPotential = 1 + MicroVolt ElectricPotential = 1000 * NanoVolt + MilliVolt ElectricPotential = 1000 * MicroVolt + Volt ElectricPotential = 1000 * MilliVolt + KiloVolt ElectricPotential = 1000 * Volt + MegaVolt ElectricPotential = 1000 * KiloVolt + GigaVolt ElectricPotential = 1000 * MegaVolt +) + +// ElectricResistance is a measurement of the difficulty to pass an electric +// current through a conductor stored as an int64 nano Ohm. +// +// The highest representable value is 9.2GΩ. +type ElectricResistance int64 + +// String returns the resistance formatted as a string in Ohm. +func (e ElectricResistance) String() string { + return nanoAsString(int64(e)) + "Ω" +} + +const ( + // Ohm is V/A, kg⋅m²/s³/A². + NanoOhm ElectricResistance = 1 + MicroOhm ElectricResistance = 1000 * NanoOhm + MilliOhm ElectricResistance = 1000 * MicroOhm + Ohm ElectricResistance = 1000 * MilliOhm + KiloOhm ElectricResistance = 1000 * Ohm + MegaOhm ElectricResistance = 1000 * KiloOhm + GigaOhm ElectricResistance = 1000 * MegaOhm +) + +// Force is a measurement of interaction that will change the motion of an +// object stored as an int64 nano Newton. +// +// A measurement of Force is a vector and has a direction but this unit only +// represents the magnitude. The orientation needs to be stored as a Quaternion +// independently. +// +// The highest representable value is 9.2TN. +type Force int64 + +// String returns the force formatted as a string in Newton. +func (f Force) String() string { + return nanoAsString(int64(f)) + "N" +} + +const ( + // Newton is kg⋅m/s². + NanoNewton Force = 1 + MicroNewton Force = 1000 * NanoNewton + MilliNewton Force = 1000 * MicroNewton + Newton Force = 1000 * MilliNewton + KiloNewton Force = 1000 * Newton + MegaNewton Force = 1000 * KiloNewton + GigaNewton Force = 1000 * MegaNewton + + EarthGravity Force = 9806650 * MicroNewton + + // Conversion between Newton and imperial units. + // Pound is both a unit of mass and weight (force). The suffix Force is added + // to disambiguate the measurement it represents. + PoundForce Force = 4448221615261 * NanoNewton +) + +// Frequency is a measurement of cycle per second, stored as an int32 micro +// Hertz. +// +// The highest representable value is 9.2THz. +type Frequency int64 + +// String returns the frequency formatted as a string in Hertz. +func (f Frequency) String() string { + return microAsString(int64(f)) + "Hz" +} + +// Duration returns the duration of one cycle at this frequency. +func (f Frequency) Duration() time.Duration { + // Note: Duration() should have been named Period(). + // TODO(maruel): Rounding should be fine-tuned. + return time.Second * time.Duration(Hertz) / time.Duration(f) +} + +// PeriodToFrequency returns the frequency for a period of this interval. +func PeriodToFrequency(t time.Duration) Frequency { + return Frequency(time.Second) * Hertz / Frequency(t) +} + +const ( + // Hertz is 1/s. + MicroHertz Frequency = 1 + MilliHertz Frequency = 1000 * MicroHertz + Hertz Frequency = 1000 * MilliHertz + KiloHertz Frequency = 1000 * Hertz + MegaHertz Frequency = 1000 * KiloHertz + GigaHertz Frequency = 1000 * MegaHertz + TeraHertz Frequency = 1000 * GigaHertz +) + +// Mass is a measurement of mass stored as an int64 nano gram. +// +// This is one of the base unit in the International System of Units. +// +// The highest representable value is 9.2Gg. +type Mass int64 + +// String returns the mass formatted as a string in gram. +func (m Mass) String() string { + return nanoAsString(int64(m)) + "g" +} + +const ( + NanoGram Mass = 1 + MicroGram Mass = 1000 * NanoGram + MilliGram Mass = 1000 * MicroGram + Gram Mass = 1000 * MilliGram + KiloGram Mass = 1000 * Gram + MegaGram Mass = 1000 * KiloGram + GigaGram Mass = 1000 * MegaGram + Tonne Mass = MegaGram + + // Conversion between Gram and imperial units. + // Ounce is both a unit of mass, weight (force) or volume depending on + // context. The suffix Mass is added to disambiguate the measurement it + // represents. + OunceMass Mass = 28349523125 * NanoGram + // Pound is both a unit of mass and weight (force). The suffix Mass is added + // to disambiguate the measurement it represents. + PoundMass Mass = 16 * OunceMass + Slug Mass = 14593903 * MilliGram +) + +// Pressure is a measurement of force applied to a surface per unit +// area (stress) stored as an int64 nano Pascal. +// +// The highest representable value is 9.2GPa. +type Pressure int64 + +// String returns the pressure formatted as a string in Pascal. +func (p Pressure) String() string { + return nanoAsString(int64(p)) + "Pa" +} + +const ( + // Pascal is N/m², kg/m/s². + NanoPascal Pressure = 1 + MicroPascal Pressure = 1000 * NanoPascal + MilliPascal Pressure = 1000 * MicroPascal + Pascal Pressure = 1000 * MilliPascal + KiloPascal Pressure = 1000 * Pascal + MegaPascal Pressure = 1000 * KiloPascal + GigaPascal Pressure = 1000 * MegaPascal +) + +// RelativeHumidity is a humidity level measurement stored as an int32 fixed +// point integer at a precision of 0.00001%rH. +// +// Valid values are between 0% and 100%. +type RelativeHumidity int32 + +// String returns the humidity formatted as a string. +func (r RelativeHumidity) String() string { + r /= MilliRH + frac := int(r % 10) + if frac == 0 { + return strconv.Itoa(int(r)/10) + "%rH" + } + if frac < 0 { + frac = -frac + } + return strconv.Itoa(int(r)/10) + "." + strconv.Itoa(frac) + "%rH" +} + +const ( + TenthMicroRH RelativeHumidity = 1 // 0.00001%rH + MicroRH RelativeHumidity = 10 * TenthMicroRH // 0.0001%rH + MilliRH RelativeHumidity = 1000 * MicroRH // 0.1%rH + PercentRH RelativeHumidity = 10 * MilliRH // 1%rH +) + +// Speed is a measurement of magnitude of velocity stored as an int64 nano +// Metre per Second. +// +// The highest representable value is 9.2Gm/s. +type Speed int64 + +// String returns the speed formatted as a string in m/s. +func (s Speed) String() string { + return nanoAsString(int64(s)) + "m/s" +} + +const ( + // MetrePerSecond is m/s. + NanoMetrePerSecond Speed = 1 + MicroMetrePerSecond Speed = 1000 * NanoMetrePerSecond + MilliMetrePerSecond Speed = 1000 * MicroMetrePerSecond + MetrePerSecond Speed = 1000 * MilliMetrePerSecond + KiloMetrePerSecond Speed = 1000 * MetrePerSecond + MegaMetrePerSecond Speed = 1000 * KiloMetrePerSecond + GigaMetrePerSecond Speed = 1000 * MegaMetrePerSecond + + LightSpeed Speed = 299792458 * MetrePerSecond + + KilometrePerHour Speed = 277777778 * NanoMetrePerSecond + MilePerHour Speed = 447040 * MicroMetrePerSecond + FootPerSecond Speed = 304800 * MicroMetrePerSecond +) + +// Temperature is a measurement of hotness stored as a nano kelvin. +// +// Negative values are invalid. +// +// The highest representable value is 9.2GK. +type Temperature int64 + +// String returns the temperature formatted as a string in °Celsius. +func (t Temperature) String() string { + return nanoAsString(int64(t-ZeroCelsius)) + "°C" +} + +const ( + NanoKelvin Temperature = 1 + MicroKelvin Temperature = 1000 * NanoKelvin + MilliKelvin Temperature = 1000 * MicroKelvin + Kelvin Temperature = 1000 * MilliKelvin + KiloKelvin Temperature = 1000 * Kelvin + MegaKelvin Temperature = 1000 * KiloKelvin + GigaKelvin Temperature = 1000 * MegaKelvin + + // Conversion between Kelvin and Celsius. + ZeroCelsius Temperature = 273150 * MilliKelvin + MilliCelsius Temperature = MilliKelvin + Celsius Temperature = Kelvin + + // Conversion between Kelvin and Fahrenheit. + ZeroFahrenheit Temperature = 255372 * MilliKelvin + MilliFahrenheit Temperature = 555555 * NanoKelvin + Fahrenheit Temperature = 555555555 * NanoKelvin +) + +// Power is a measurement of power stored as a nano watts. +// +// The highest representable value is 9.2GW. +type Power int64 + +// String returns the power formatted as a string in watts. +func (p Power) String() string { + return nanoAsString(int64(p)) + "W" +} + +const ( + // Watt is unit of power J/s, kg⋅m²⋅s⁻³ + NanoWatt Power = 1 + MicroWatt Power = 1000 * NanoWatt + MilliWatt Power = 1000 * MicroWatt + Watt Power = 1000 * MilliWatt + KiloWatt Power = 1000 * Watt + MegaWatt Power = 1000 * KiloWatt + GigaWatt Power = 1000 * MegaWatt +) + +// Energy is a measurement of work stored as a nano joules. +// +// The highest representable value is 9.2GJ. +type Energy int64 + +// String returns the energy formatted as a string in Joules. +func (e Energy) String() string { + return nanoAsString(int64(e)) + "J" +} + +const ( + // Joule is a unit of work. kg⋅m²⋅s⁻² + NanoJoule Energy = 1 + MicroJoule Energy = 1000 * NanoJoule + MilliJoule Energy = 1000 * MicroJoule + Joule Energy = 1000 * MilliJoule + KiloJoule Energy = 1000 * Joule + MegaJoule Energy = 1000 * KiloJoule + GigaJoule Energy = 1000 * MegaJoule +) + +// ElectricalCapacitance is a measurement of capacitance stored as a pico farad. +// +// The highest representable value is 9.2MF. +type ElectricalCapacitance int64 + +// String returns the energy formatted as a string in Farad. +func (c ElectricalCapacitance) String() string { + return picoAsString(int64(c)) + "F" +} + +const ( + // Farad is a unit of capacitance. kg⁻¹⋅m⁻²⋅s⁴A² + PicoFarad ElectricalCapacitance = 1 + NanoFarad ElectricalCapacitance = 1000 * PicoFarad + MicroFarad ElectricalCapacitance = 1000 * NanoFarad + MilliFarad ElectricalCapacitance = 1000 * MicroFarad + Farad ElectricalCapacitance = 1000 * MilliFarad + KiloFarad ElectricalCapacitance = 1000 * Farad + MegaFarad ElectricalCapacitance = 1000 * KiloFarad +) + +// LuminousIntensity is a measurement of the quantity of visible light energy +// emitted per unit solid angle with wavelength power weighted by a luminosity +// function which represents the human eye's response to different wavelengths. +// The CIE 1931 luminosity function is the SI standard for candela. +// +// LuminousIntensity is stored as nano candela. +// +// This is one of the base unit in the International System of Units. +// +// The highest representable value is 9.2Gcd. +type LuminousIntensity int64 + +// String returns the energy formatted as a string in Candela. +func (l LuminousIntensity) String() string { + return nanoAsString(int64(l)) + "cd" +} + +const ( + // Candela is a unit of luminous intensity. cd + NanoCandela LuminousIntensity = 1 + MicroCandela LuminousIntensity = 1000 * NanoCandela + MilliCandela LuminousIntensity = 1000 * MicroCandela + Candela LuminousIntensity = 1000 * MilliCandela + KiloCandela LuminousIntensity = 1000 * Candela + MegaCandela LuminousIntensity = 1000 * KiloCandela + GigaCandela LuminousIntensity = 1000 * MegaCandela +) + +// LuminousFlux is a measurement of total quantity of visible light energy +// emitted with wavelength power weighted by a luminosity function which +// represents a model of the human eye's response to different wavelengths. +// The CIE 1931 luminosity function is the standard for lumens. +// +// LuminousFlux is stored as nano lumens. +// +// The highest representable value is 9.2Glm. +type LuminousFlux int64 + +// String returns the energy formatted as a string in Lumens. +func (f LuminousFlux) String() string { + return nanoAsString(int64(f)) + "lm" +} + +const ( + // Lumen is a unit of luminous flux. cd⋅sr + NanoLumen LuminousFlux = 1 + MicroLumen LuminousFlux = 1000 * NanoLumen + MilliLumen LuminousFlux = 1000 * MicroLumen + Lumen LuminousFlux = 1000 * MilliLumen + KiloLumen LuminousFlux = 1000 * Lumen + MegaLumen LuminousFlux = 1000 * KiloLumen + GigaLumen LuminousFlux = 1000 * MegaLumen +) + +// + +func prefixZeros(digits, v int) string { + // digits is expected to be around 2~3. + s := strconv.Itoa(v) + for len(s) < digits { + // O(n²) but since digits is expected to run 2~3 times at most, it doesn't + // matter. + s = "0" + s + } + return s +} + +// nanoAsString converts a value in S.I. unit in a string with the predefined +// prefix. +func nanoAsString(v int64) string { + sign := "" + if v < 0 { + if v == -9223372036854775808 { + v++ + } + sign = "-" + v = -v + } + var frac int + var base int + var precision int64 + unit := "" + switch { + case v >= 999999500000000001: + precision = v % 1000000000000000 + base = int(v / 1000000000000000) + if precision > 500000000000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "G" + case v >= 999999500000001: + precision = v % 1000000000000 + base = int(v / 1000000000000) + if precision > 500000000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "M" + case v >= 999999500001: + precision = v % 1000000000 + base = int(v / 1000000000) + if precision > 500000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "k" + case v >= 999999501: + precision = v % 1000000 + base = int(v / 1000000) + if precision > 500000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "" + case v >= 1000000: + precision = v % 1000 + base = int(v / 1000) + if precision > 500 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "m" + case v >= 1000: + frac = int(v) % 1000 + base = int(v) / 1000 + unit = "µ" + default: + if v == 0 { + return "0" + } + base = int(v) + unit = "n" + } + if frac == 0 { + return sign + strconv.Itoa(base) + unit + } + return sign + strconv.Itoa(base) + "." + prefixZeros(3, frac) + unit +} + +// microAsString converts a value in S.I. unit in a string with the predefined +// prefix. +func microAsString(v int64) string { + sign := "" + if v < 0 { + if v == -9223372036854775808 { + v++ + } + sign = "-" + v = -v + } + var frac int + var base int + var precision int64 + unit := "" + switch { + case v >= 999999500000000001: + precision = v % 1000000000000000 + base = int(v / 1000000000000000) + if precision > 500000000000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "T" + case v >= 999999500000001: + precision = v % 1000000000000 + base = int(v / 1000000000000) + if precision > 500000000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "G" + case v >= 999999500001: + precision = v % 1000000000 + base = int(v / 1000000000) + if precision > 500000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "M" + case v >= 999999501: + precision = v % 1000000 + base = int(v / 1000000) + if precision > 500000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "k" + case v >= 1000000: + precision = v % 1000 + base = int(v / 1000) + if precision > 500 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "" + case v >= 1000: + frac = int(v) % 1000 + base = int(v) / 1000 + unit = "m" + default: + if v == 0 { + return "0" + } + base = int(v) + unit = "µ" + } + if frac == 0 { + return sign + strconv.Itoa(base) + unit + } + return sign + strconv.Itoa(base) + "." + prefixZeros(3, frac) + unit +} + +// picoAsString converts a value in S.I. unit in a string with the predefined +// prefix. +func picoAsString(v int64) string { + sign := "" + if v < 0 { + if v == -9223372036854775808 { + v++ + } + sign = "-" + v = -v + } + var frac int + var base int + var precision int64 + unit := "" + switch { + case v >= 999999500000000001: + precision = v % 1000000000000000 + base = int(v / 1000000000000000) + if precision > 500000000000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "M" + case v >= 999999500000001: + precision = v % 1000000000000 + base = int(v / 1000000000000) + if precision > 500000000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "k" + case v >= 999999500001: + precision = v % 1000000000 + base = int(v / 1000000000) + if precision > 500000000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "" + case v >= 999999501: + precision = v % 1000000 + base = int(v / 1000000) + if precision > 500000 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "m" + case v >= 1000000: + precision = v % 1000 + base = int(v / 1000) + if precision > 500 { + base++ + } + frac = (base % 1000) + base = base / 1000 + unit = "µ" + case v >= 1000: + frac = int(v) % 1000 + base = int(v) / 1000 + unit = "n" + default: + if v == 0 { + return "0" + } + base = int(v) + unit = "p" + } + if frac == 0 { + return sign + strconv.Itoa(base) + unit + } + return sign + strconv.Itoa(base) + "." + prefixZeros(3, frac) + unit +} + +// Decimal is the exact representation of decimal number. +type decimal struct { + // digits hold the string representation of the significant decimal digits. + digits string + // exponent is the left or right decimal shift. (powers of ten). + exp int + // neg it true if the number is negative. + neg bool +} + +// Positive powers of 10 in the form such that powerOF10[index] = 10^index. +var powerOf10 = [...]uint64{ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, +} + +// Maximum value for a int64. +const maxInt64 = (1<<63 - 1) + +var maxUint64Str = "9223372036854775807" + +// Converts from decimal to int64, using the decimal.digits character values and +// converting to a intermediate unit64. +// Scale is combined with the decimal exponent to maximise the resolution and is +// in powers of ten. +func dtoi(d decimal, scale int) (int64, error) { + // Use uint till the last as it allows checks for overflows. + var u uint64 + for i := 0; i < len(d.digits); i++ { + // Check that is is a digit. + if d.digits[i] >= '0' && d.digits[i] <= '9' { + // '0' = 0x30 '1' = 0x31 ...etc. + digit := d.digits[i] - '0' + // *10 is decimal shift left. + u *= 10 + check := u + uint64(digit) + // Check should always be larger than u unless we have overflowed. + // Similarly if check > max it will overflow when converted to int64. + if check < u || check > maxInt64 { + if d.neg { + return -maxInt64, &parseError{ + s: "-" + maxUint64Str, + err: errors.New("overflows minimum is"), + } + } + return maxInt64, &parseError{ + s: maxUint64Str, + err: errors.New("overflows maximum is"), + } + } + u = check + } else { + // Should not get here if used atod to generate the decimal. + return 0, &parseError{err: errors.New("not a number")} + } + } + + // Get the total magnitude of the number. + // a^x * b^y = a*b^(x+y) since scale is of the order unity this becomes + // 1^x * b^y = b^(x+y). + // mag must be positive to use as index in to powerOf10 array. + mag := d.exp + scale + if mag < 0 { + mag *= -1 + } + if mag > 18 { + return 0, errors.New("exponent exceeds int64") + } + // Divide is = 10^(-mag) + if d.exp+scale < 0 { + u = (u + powerOf10[mag]/2) / powerOf10[mag] + } else { + check := u * powerOf10[mag] + if check < u || check > maxInt64 { + if d.neg { + return -maxInt64, &parseError{ + s: "-" + maxUint64Str, + err: errors.New("overflows minimum is"), + } + } + return maxInt64, &parseError{ + s: maxUint64Str, + err: errors.New("overflows maximum is"), + } + } + u *= powerOf10[mag] + } + + n := int64(u) + if d.neg { + n *= -1 + } + return n, nil +} + +// Converts a string to a decimal form. The return int is how many bytes of the +// string are numeric. The string may contain +-0 prefixes and arbitrary +// suffixes as trailing non number characters are ignored. +// Significant digits are stored without leading or trailing zeros, rather an +// exponent is used. +func atod(s string) (decimal, int, error) { + var d decimal + start := 0 + dp := 0 + end := len(s) + seenDigit := false + seenZero := false + isPoint := false + seenPlus := false + + // Strip leading zeros, +/- and mark DP. + for i := 0; i < len(s); i++ { + switch { + case s[i] == '-': + if seenDigit { + end = i + break + } + if seenPlus { + return decimal{}, 0, &parseError{ + s: s, + err: errors.New("can't contain both plus and minus symbols"), + } + } + if d.neg { + return decimal{}, 0, &parseError{ + s: s, + err: errors.New("multiple minus symbols"), + } + } + d.neg = true + start++ + case s[i] == '+': + if seenDigit { + end = i + break + } + if d.neg { + return decimal{}, 0, &parseError{ + s: s, + err: errors.New("can't contain both plus and minus symbols"), + } + } + if seenPlus { + return decimal{}, 0, &parseError{ + s: s, + err: errors.New("multiple plus symbols"), + } + } + seenPlus = true + start++ + case s[i] == '.': + if isPoint { + return decimal{}, 0, &parseError{ + s: s, + err: errors.New("multiple decimal points"), + } + } + isPoint = true + dp = i + if !seenDigit { + start++ + } + case s[i] == '0': + if !seenDigit { + start++ + } + seenZero = true + case s[i] >= '1' && s[i] <= '9': + seenDigit = true + default: + if !seenDigit && !seenZero { + return decimal{}, 0, &parseError{ + s: s, + err: errors.New("is not a number"), + } + } + end = i + break + } + } + + last := end + seenDigit = false + exp := 0 + // Strip non significant zeros to find base exponent. + for i := end - 1; i > start-1; i-- { + switch { + case s[i] >= '1' && s[i] <= '9': + seenDigit = true + case s[i] == '.': + if !seenDigit { + end-- + } + case s[i] == '0': + if !seenDigit { + if i > dp { + end-- + } + if i <= dp || dp == 0 { + exp++ + } + } + default: + last-- + end-- + } + } + + if dp > start && dp < end { + // Concatenate with out decimal point. + d.digits = s[start:dp] + s[dp+1:end] + } else { + d.digits = s[start:end] + } + if !isPoint { + d.exp = exp + } else { + ttl := dp - start + length := len(d.digits) + if ttl > 0 { + d.exp = ttl - length + } else { + d.exp = ttl - length + 1 + } + } + return d, last, nil +} + +type parseError struct { + s string + position int + err error +} + +func (p *parseError) Error() string { + if p.err == nil { + return "parse error" + } + if p.s == "" { + return "parse error: " + p.err.Error() + } + return "parse error: " + p.err.Error() + ": \"" + p.s + "\"" +} + +func noUnits(s string) error { + return &parseError{s: s, err: errors.New("no units provided, need")} +} + +type prefix int + +const ( + pico prefix = -12 + nano prefix = -9 + micro prefix = -6 + milli prefix = -3 + none prefix = 0 + deca prefix = 1 + hecto prefix = 2 + kilo prefix = 3 + mega prefix = 6 + giga prefix = 9 + tera prefix = 12 +) + +func parseSIPrefix(r rune) (prefix, int) { + switch r { + case 'p': + return pico, len("p") + case 'n': + return nano, len("n") + case 'u': + return micro, len("u") + case 'µ': + return micro, len("µ") + case 'm': + return milli, len("m") + case 'k': + return kilo, len("k") + case 'M': + return mega, len("M") + case 'G': + return giga, len("G") + case 'T': + return tera, len("T") + default: + return none, 0 + } +} diff --git a/vendor/periph.io/x/periph/conn/pin/func.go b/vendor/periph.io/x/periph/conn/pin/func.go new file mode 100644 index 0000000..3ebd2ce --- /dev/null +++ b/vendor/periph.io/x/periph/conn/pin/func.go @@ -0,0 +1,58 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pin + +import ( + "strconv" + "strings" +) + +// Func is a pin function. +// +// The Func format must be "[A-Z]+", "[A-Z]+_[A-Z]+" or exceptionally +// "(In|Out)/(Low|High)". +type Func string + +// FuncNone is returned by PinFunc.Func() for a Pin without an active +// functionality. +const FuncNone Func = "" + +// Specialize converts a "BUS_LINE" function and appends the bug number and +// line number, to look like "BUS0_LINE1". +// +// Use -1 to not add a bus or line number. +func (f Func) Specialize(b, l int) Func { + if f == FuncNone { + return FuncNone + } + if b != -1 { + parts := strings.SplitN(string(f), "_", 2) + if len(parts) == 1 { + return FuncNone + } + f = Func(parts[0] + strconv.Itoa(b) + "_" + parts[1]) + } + if l != -1 { + f += Func(strconv.Itoa(l)) + } + return f +} + +// Generalize is the reverse of Specialize(). +func (f Func) Generalize() Func { + parts := strings.SplitN(string(f), "_", 2) + f = Func(strings.TrimRightFunc(parts[0], isNum)) + if len(parts) == 2 { + f += "_" + f += Func(strings.TrimRightFunc(parts[1], isNum)) + } + return f +} + +// + +func isNum(r rune) bool { + return r >= '0' && r <= '9' +} diff --git a/vendor/periph.io/x/periph/conn/pin/pin.go b/vendor/periph.io/x/periph/conn/pin/pin.go new file mode 100644 index 0000000..9cf60bd --- /dev/null +++ b/vendor/periph.io/x/periph/conn/pin/pin.go @@ -0,0 +1,139 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package pin declare well known pins. +// +// pin is about physical pins, not about their logical function. +// +// While not a protocol strictly speaking, these are "well known constants". +package pin + +import ( + "errors" + + "periph.io/x/periph/conn" +) + +// These are well known pins. +var ( + INVALID *BasicPin // Either floating or invalid pin + GROUND *BasicPin // Ground + V1_8 *BasicPin // 1.8V (filtered) + V2_8 *BasicPin // 2.8V (filtered) + V3_3 *BasicPin // 3.3V (filtered) + V5 *BasicPin // 5V (filtered) + DC_IN *BasicPin // DC IN; this is normally the 5V input + BAT_PLUS *BasicPin // LiPo Battery + connector +) + +// Pin is the minimal common interface shared between gpio.PinIO and +// analog.PinIO. +type Pin interface { + conn.Resource + // Name returns the name of the pin. + Name() string + // Number returns the logical pin number or a negative number if the pin is + // not a GPIO, e.g. GROUND, V3_3, etc. + Number() int + // Function returns a user readable string representation of what the pin is + // configured to do. Common case is In and Out but it can be bus specific pin + // name. + // + // Deprecated: Use PinFunc.Func. Will be removed in v4. + Function() string +} + +// PinFunc is a supplementary interface that enables specifically querying for +// the pin function. +// +// TODO(maruel): It will be merged into interface Pin for v4. +type PinFunc interface { + // Func returns the pin's current function. + // + // The returned value may be specialized or generalized, depending on the + // actual port. For example it will likely be generalized for ports served + // over USB (like a FT232H with D0 set as SPI_MOSI) but specialized for + // ports on the base board (like a RPi3 with GPIO10 set as SPI0_MOSI). + Func() Func + // SupportedFuncs returns the possible functions this pin support. + // + // Do not mutate the returned slice. + SupportedFuncs() []Func + // SetFunc sets the pin function. + // + // Example use is to reallocate a RPi3's GPIO14 active function between + // UART0_TX and UART1_TX. + SetFunc(f Func) error +} + +// + +// BasicPin implements Pin as a static pin. +// +// It doesn't have a usable functionality. +type BasicPin struct { + N string +} + +// String implements conn.Resource. +func (b *BasicPin) String() string { + return b.N +} + +// Halt implements conn.Resource. +func (b *BasicPin) Halt() error { + return nil +} + +// Name implements Pin. +func (b *BasicPin) Name() string { + return b.N +} + +// Number implements Pin. +// +// Returns -1 as pin number. +func (b *BasicPin) Number() int { + return -1 +} + +// Function implements Pin. +// +// Returns "" as pin function. +func (b *BasicPin) Function() string { + return "" +} + +// Func implements PinFunc. +// +// Returns FuncNone as pin function. +func (b *BasicPin) Func() Func { + return FuncNone +} + +// SupportedFuncs implements PinFunc. +// +// Returns nil. +func (b *BasicPin) SupportedFuncs() []Func { + return nil +} + +// SetFunc implements PinFunc. +func (b *BasicPin) SetFunc(f Func) error { + return errors.New("pin: can't change static pin function") +} + +func init() { + INVALID = &BasicPin{N: "INVALID"} + GROUND = &BasicPin{N: "GROUND"} + V1_8 = &BasicPin{N: "1.8V"} + V2_8 = &BasicPin{N: "2.8V"} + V3_3 = &BasicPin{N: "3.3V"} + V5 = &BasicPin{N: "5V"} + DC_IN = &BasicPin{N: "DC_IN"} + BAT_PLUS = &BasicPin{N: "BAT+"} +} + +var _ Pin = INVALID +var _ PinFunc = INVALID diff --git a/vendor/periph.io/x/periph/conn/pin/pinreg/doc.go b/vendor/periph.io/x/periph/conn/pin/pinreg/doc.go new file mode 100644 index 0000000..bed7586 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/pin/pinreg/doc.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package pinreg is a registry for the physical headers (made up of pins) on +// a host. +package pinreg diff --git a/vendor/periph.io/x/periph/conn/pin/pinreg/pinreg.go b/vendor/periph.io/x/periph/conn/pin/pinreg/pinreg.go new file mode 100644 index 0000000..7ccdbfb --- /dev/null +++ b/vendor/periph.io/x/periph/conn/pin/pinreg/pinreg.go @@ -0,0 +1,148 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pinreg + +import ( + "errors" + "strconv" + "sync" + + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/pin" +) + +// All contains all the on-board headers on a micro computer. +// +// The map key is the header name, e.g. "P1" or "EULER" and the value is a +// slice of slice of pin.Pin. For a 2x20 header, it's going to be a slice of +// [20][2]pin.Pin. +func All() map[string][][]pin.Pin { + mu.Lock() + defer mu.Unlock() + out := make(map[string][][]pin.Pin, len(allHeaders)) + for k, v := range allHeaders { + outV := make([][]pin.Pin, len(v)) + for i, w := range v { + outW := make([]pin.Pin, len(w)) + copy(outW, w) + outV[i] = outW + } + out[k] = outV + } + return out +} + +// Position returns the position on a pin if found. +// +// The header and the pin number. Pin numbers are 1-based. +// +// Returns "", 0 if not connected. +func Position(p pin.Pin) (string, int) { + mu.Lock() + defer mu.Unlock() + pos, _ := byPin[realPin(p).Name()] + return pos.name, pos.number +} + +// IsConnected returns true if the pin is on a header. +func IsConnected(p pin.Pin) bool { + _, i := Position(p) + return i != 0 +} + +// Register registers a physical header. +// +// It automatically registers all gpio pins to gpioreg. +func Register(name string, allPins [][]pin.Pin) error { + mu.Lock() + defer mu.Unlock() + if _, ok := allHeaders[name]; ok { + return errors.New("pinreg: header " + strconv.Quote(name) + " was already registered") + } + for i, line := range allPins { + for j, pin := range line { + if pin == nil || len(pin.Name()) == 0 { + return errors.New("pinreg: invalid pin on header " + name + "[" + strconv.Itoa(i+1) + "][" + strconv.Itoa(j+1) + "]") + } + } + } + allHeaders[name] = allPins + number := 1 + for _, line := range allPins { + for _, p := range line { + byPin[realPin(p).Name()] = position{name, number} + number++ + } + } + + count := 0 + for _, row := range allPins { + for _, p := range row { + count++ + if _, ok := p.(gpio.PinIO); ok { + if err := gpioreg.RegisterAlias(name+"_"+strconv.Itoa(count), p.Name()); err != nil { + // Unregister as much as possible. + _ = unregister(name) + return errors.New("pinreg: " + err.Error()) + } + } + } + } + + return nil +} + +// Unregister removes a previously registered header. +// +// This can happen when an USB device, which exposed an header, is unplugged. +// This is also useful for unit testing. +func Unregister(name string) error { + mu.Lock() + defer mu.Unlock() + return unregister(name) +} + +// + +type position struct { + name string // Header name + number int // Pin number +} + +var ( + mu sync.Mutex + allHeaders = map[string][][]pin.Pin{} // every known headers as per internal lookup table + byPin = map[string]position{} // GPIO pin name to position +) + +func unregister(name string) error { + if hdr, ok := allHeaders[name]; ok { + var err error + delete(allHeaders, name) + count := 0 + for _, row := range hdr { + for _, p := range row { + count++ + if _, ok := p.(gpio.PinIO); ok { + if err1 := gpioreg.Unregister(name + "_" + strconv.Itoa(count)); err1 != nil && err == nil { + // Continue unregistering as much as possible. + err = errors.New("pinreg: " + err1.Error()) + } + } + } + } + return err + } + return errors.New("pinreg: can't unregister unknown header name " + strconv.Quote(name)) +} + +// realPin returns the real pin from an alias. +func realPin(p pin.Pin) pin.Pin { + if r, ok := p.(gpio.RealPin); ok { + p = r.Real() + } + return p +} diff --git a/vendor/periph.io/x/periph/conn/spi/func.go b/vendor/periph.io/x/periph/conn/spi/func.go new file mode 100644 index 0000000..cc10592 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/spi/func.go @@ -0,0 +1,14 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package spi + +import "periph.io/x/periph/conn/pin" + +const ( + CLK pin.Func = "SPI_CLK" // Clock + CS pin.Func = "SPI_CS" // Chip select + MISO pin.Func = "SPI_MISO" // Master in + MOSI pin.Func = "SPI_MOSI" // Master out +) diff --git a/vendor/periph.io/x/periph/conn/spi/spi.go b/vendor/periph.io/x/periph/conn/spi/spi.go new file mode 100644 index 0000000..b1a2159 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/spi/spi.go @@ -0,0 +1,187 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package spi defines the API to communicate with devices over the SPI +// protocol. +// +// As described in https://periph.io/x/periph/conn#hdr-Concepts, periph.io uses +// the concepts of Bus, Port and Conn. +// +// In the package spi, 'Bus' is not exposed, as it would be SPI bus number +// without a CS line, for example on linux asking for "/dev/spi0" without the +// ".0" suffix. +// +// The OS doesn't allow that so it is counter productive to express this at the +// API layer, so 'Port' is exposed directly instead. +// +// Use Port.Connect() converts the uninitialized Port into a Conn. +// +// See https://en.wikipedia.org/wiki/Serial_Peripheral_Interface for more +// information. +package spi + +import ( + "io" + "strconv" + + "periph.io/x/periph/conn" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/physic" +) + +// Mode determines how communication is done. +// +// The bits can be OR'ed to change the parameters used for +// communication. +// +type Mode int + +// Mode determines the SPI communication parameters. +// +// CPOL means the clock polarity. Idle is High when set. +// +// CPHA is the clock phase, sample on trailing edge when set. +const ( + Mode0 Mode = 0x0 // CPOL=0, CPHA=0 + Mode1 Mode = 0x1 // CPOL=0, CPHA=1 + Mode2 Mode = 0x2 // CPOL=1, CPHA=0 + Mode3 Mode = 0x3 // CPOL=1, CPHA=1 + + // HalfDuplex specifies that MOSI and MISO use the same wire, and that only + // one duplex is used at a time. + HalfDuplex Mode = 0x4 + // NoCS request the driver to not use the CS line. + NoCS Mode = 0x8 + // LSBFirst requests the words to be encoded in little endian instead of the + // default big endian. + LSBFirst = 0x10 +) + +func (m Mode) String() string { + s := "" + switch m & Mode3 { + case Mode0: + s = "Mode0" + case Mode1: + s = "Mode1" + case Mode2: + s = "Mode2" + case Mode3: + s = "Mode3" + } + m &^= Mode3 + if m&HalfDuplex != 0 { + s += "|HalfDuplex" + } + m &^= HalfDuplex + if m&NoCS != 0 { + s += "|NoCS" + } + m &^= NoCS + if m&LSBFirst != 0 { + s += "|LSBFirst" + } + m &^= LSBFirst + if m != 0 { + s += "|0x" + s += strconv.FormatUint(uint64(m), 16) + } + return s +} + +// Packet represents one packet when sending multiple packets as a transaction. +type Packet struct { + // W and R are the output and input data. When HalfDuplex is specified to + // Connect, only one of the two can be set. + W, R []byte + // BitsPerWord overrides the default bits per word value set in Connect. + BitsPerWord uint8 + // KeepCS tells the driver to keep CS asserted after this packet is + // completed. This can be leveraged to create long transaction as multiple + // packets like to use 9 bits commands then 8 bits data. + // + // Normally during a spi.Conn.TxPackets() call, KeepCS should be set to true + // for all packets except the last one. If the last one is set to true, the + // CS line stays asserted, leaving the transaction hanging on the bus. + // + // KeepCS is ignored when NoCS was specified to Connect. + KeepCS bool +} + +// Conn defines the interface a concrete SPI driver must implement. +// +// Implementers can optionally implement io.Writer and io.Reader for +// unidirectional operation. +type Conn interface { + conn.Conn + // TxPackets does multiple operations over the SPI connection. + // + // The maximum number of bytes can be limited depending on the driver. Query + // conn.Limits.MaxTxSize() can be used to determine the limit. + // + // If the last packet has KeepCS:true, the CS line stays asserted. This + // enables doing SPI transaction over multiple calls. + // + // Conversely, if any packet beside the last one has KeepCS:false, the CS + // line will blip for a short amount of time to force a new transaction. + // + // It was observed on RPi3 hardware to have a one clock delay between each + // packet. + TxPackets(p []Packet) error +} + +// Port is the interface to be provided to device drivers. +// +// The device driver, that is the driver for the peripheral connected over +// this port, calls Connect() to retrieve a configured connection as Conn. +type Port interface { + String() string + // Connect sets the communication parameters of the connection for use by a + // device. + // + // The device driver must call this function exactly once. + // + // f must specify the maximum rated speed by the device's spec. The lowest + // speed between the port speed and the device speed is selected. Use 0 for f + // if there is no known maximum value for this device. + // + // mode specifies the clock and signal polarities, if the port is using half + // duplex (shared MISO and MOSI) or if CS is not needed. + // + // bits is the number of bits per word. Generally you should use 8. + Connect(f physic.Frequency, mode Mode, bits int) (Conn, error) +} + +// PortCloser is a SPI port that can be closed. +// +// This interface is meant to be handled by the application. +type PortCloser interface { + io.Closer + Port + // LimitSpeed sets the maximum port speed. + // + // It lets an application use a device at a lower speed than the maximum + // speed as rated by the device driver. This is useful for example when the + // wires are long or the connection is of poor quality. + // + // This function can be called multiple times and resets the previous value. + // 0 is not a valid value for f. The lowest speed between the port speed and + // the device speed is selected. + LimitSpeed(f physic.Frequency) error +} + +// Pins defines the pins that a SPI port interconnect is using on the host. +// +// It is expected that a implementer of ConnCloser or Conn also implement Pins +// but this is not a requirement. +type Pins interface { + // CLK returns the SCK (clock) pin. + CLK() gpio.PinOut + // MOSI returns the SDO (master out, slave in) pin. + MOSI() gpio.PinOut + // MISO returns the SDI (master in, slave out) pin. + MISO() gpio.PinIn + // CS returns the CSN (chip select) pin. + CS() gpio.PinOut +} diff --git a/vendor/periph.io/x/periph/conn/spi/spireg/spireg.go b/vendor/periph.io/x/periph/conn/spi/spireg/spireg.go new file mode 100644 index 0000000..dbc74f7 --- /dev/null +++ b/vendor/periph.io/x/periph/conn/spi/spireg/spireg.go @@ -0,0 +1,262 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package spireg defines the SPI registry for SPI ports discovered on the host. +// +// SPI ports discovered on the host are automatically registered in the SPI +// registry by host.Init(). +package spireg + +import ( + "errors" + "strconv" + "strings" + "sync" + + "periph.io/x/periph/conn/spi" +) + +// Opener opens an handle to a port. +// +// It is provided by the actual port driver. +type Opener func() (spi.PortCloser, error) + +// Ref references a SPI port. +// +// It is returned by All() to enumerate all registered ports. +type Ref struct { + // Name of the port. + // + // It must not be a sole number. It must be unique across the host. + Name string + // Aliases are the alternative names that can be used to reference this port. + Aliases []string + // Number of the bus or -1 if the bus doesn't have any "native" number. + // + // Buses provided by the CPU normally have a 0 based number. Buses provided + // via an addon (like over USB) generally are not numbered. + // + // The port is a bus number plus a CS line. + Number int + // Open is the factory to open an handle to this SPI port. + Open Opener +} + +// Open opens a SPI port by its name, an alias or its number and returns an +// handle to it. +// +// Specify the empty string "" to get the first available port. This is the +// recommended default value unless an application knows the exact port to use. +// +// Each port can register multiple aliases, each leading to the same port +// handle. +// +// "Bus number" is a generic concept that is highly dependent on the platform +// and OS. On some platform, the first port may have the number 0, 1 or as high +// as 32766. Bus numbers are not necessarily continuous and may not start at 0. +// It was observed that the bus number as reported by the OS may change across +// OS revisions. +// +// A SPI port is constructed of the bus number and the chip select (CS) number. +// +// When the SPI port is provided by an off board plug and play bus like USB via +// a FT232H USB device, there can be no associated number. +func Open(name string) (spi.PortCloser, error) { + var r *Ref + var err error + func() { + mu.Lock() + defer mu.Unlock() + if len(byName) == 0 { + err = errors.New("spireg: no port found; did you forget to call Init()?") + return + } + if len(name) == 0 { + r = getDefault() + return + } + // Try by name, by alias, by number. + if r = byName[name]; r == nil { + if r = byAlias[name]; r == nil { + if i, err2 := strconv.Atoi(name); err2 == nil { + r = byNumber[i] + } + } + } + }() + if err != nil { + return nil, err + } + if r == nil { + return nil, errors.New("spireg: can't open unknown port: " + strconv.Quote(name)) + } + return r.Open() +} + +// All returns a copy of all the registered references to all know SPI ports +// available on this host. +// +// The list is sorted by the port name. +func All() []*Ref { + mu.Lock() + defer mu.Unlock() + out := make([]*Ref, 0, len(byName)) + for _, v := range byName { + r := &Ref{Name: v.Name, Aliases: make([]string, len(v.Aliases)), Number: v.Number, Open: v.Open} + copy(r.Aliases, v.Aliases) + out = insertRef(out, r) + } + return out +} + +// Register registers a SPI port. +// +// Registering the same port name twice is an error, e.g. o.Name(). o.Number() +// can be -1 to signify that the port doesn't have an inherent "bus number". A +// good example is a port provided over a FT232H device connected on an USB bus. +// In this case, the port name should be created from the serial number of the +// device for unique identification. +// +// Only ports with the CS #0 are registered with their number. +func Register(name string, aliases []string, number int, o Opener) error { + if len(name) == 0 { + return errors.New("spireg: can't register a port with no name") + } + if o == nil { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with nil Opener") + } + if number < -1 { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with invalid port number " + strconv.Itoa(number)) + } + if _, err := strconv.Atoi(name); err == nil { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with name being only a number") + } + if strings.Contains(name, ":") { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with name containing ':'") + } + for _, alias := range aliases { + if len(alias) == 0 { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with an empty alias") + } + if name == alias { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with an alias the same as the port name") + } + if _, err := strconv.Atoi(alias); err == nil { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with an alias that is a number: " + strconv.Quote(alias)) + } + if strings.Contains(alias, ":") { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " with an alias containing ':': " + strconv.Quote(alias)) + } + } + + mu.Lock() + defer mu.Unlock() + if _, ok := byName[name]; ok { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " twice") + } + if _, ok := byAlias[name]; ok { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " twice; it is already an alias") + } + if number != -1 { + if _, ok := byNumber[number]; ok { + return errors.New("spireg: can't register port " + strconv.Quote(name) + "; port number " + strconv.Itoa(number) + " is already registered") + } + } + for _, alias := range aliases { + if _, ok := byName[alias]; ok { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already a port") + } + if _, ok := byAlias[alias]; ok { + return errors.New("spireg: can't register port " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already an alias") + } + } + + r := &Ref{Name: name, Aliases: make([]string, len(aliases)), Number: number, Open: o} + copy(r.Aliases, aliases) + byName[name] = r + if number != -1 { + byNumber[number] = r + } + for _, alias := range aliases { + byAlias[alias] = r + } + return nil +} + +// Unregister removes a previously registered SPI port. +// +// This can happen when a SPI port is exposed via an USB device and the device +// is unplugged. +func Unregister(name string) error { + mu.Lock() + defer mu.Unlock() + r := byName[name] + if r == nil { + return errors.New("spireg: can't unregister unknown port name " + strconv.Quote(name)) + } + delete(byName, name) + delete(byNumber, r.Number) + for _, alias := range r.Aliases { + delete(byAlias, alias) + } + return nil +} + +// + +var ( + mu sync.Mutex + byName = map[string]*Ref{} + // Caches + byNumber = map[int]*Ref{} + byAlias = map[string]*Ref{} +) + +// getDefault returns the Ref that should be used as the default port. +func getDefault() *Ref { + var o *Ref + if len(byNumber) == 0 { + // Fallback to use byName using a lexical sort. + name := "" + for n, o2 := range byName { + if len(name) == 0 || n < name { + o = o2 + name = n + } + } + return o + } + number := int((^uint(0)) >> 1) + for n, o2 := range byNumber { + if number > n { + number = n + o = o2 + } + } + return o +} + +func insertRef(l []*Ref, r *Ref) []*Ref { + n := r.Name + i := search(len(l), func(i int) bool { return l[i].Name > n }) + l = append(l, nil) + copy(l[i+1:], l[i:]) + l[i] = r + return l +} + +// search implements the same algorithm as sort.Search(). +// +// It was extracted to to not depend on sort, which depends on reflect. +func search(n int, f func(int) bool) int { + lo := 0 + for hi := n; lo < hi; { + if i := int(uint(lo+hi) >> 1); !f(i) { + lo = i + 1 + } else { + hi = i + } + } + return lo +} diff --git a/vendor/periph.io/x/periph/host/allwinner/a20.go b/vendor/periph.io/x/periph/host/allwinner/a20.go new file mode 100644 index 0000000..fab6595 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/a20.go @@ -0,0 +1,220 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// This file contains pin mapping information that is specific to the Allwinner +// A20 model. + +package allwinner + +import ( + "strings" + + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/sysfs" +) + +// mappingA20 describes the mapping of the A20 processor gpios to their +// alternate functions. +// +// It omits the in & out functions which are available on all gpio. +// +// The mapping comes from the datasheet page 241: +// http://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf +var mappingA20 = map[string][5]pin.Func{ + "PA0": {"ERXD3", "SPI1_CS0", "UART2_RTS", "GRXD3"}, + "PA1": {"ERXD2", "SPI1_CLK", "UART2_CTS", "GRXD2"}, + "PA2": {"ERXD1", "SPI1_MOSI", "UART2_TX", "GRXD1"}, + "PA3": {"ERXD0", "SPI1_MISO", "UART2_RX", "GRXD0"}, + "PA4": {"ETXD3", "SPI1_CS1", "", "GTXD3"}, + "PA5": {"ETXD2", "SPI3_CS0", "", "GTXD2"}, + "PA6": {"ETXD1", "SPI3_CLK", "", "GTXD1"}, + "PA7": {"ETXD0", "SPI3_MOSI", "", "GTXD0"}, + "PA8": {"ERXCK", "SPI3_MISO", "", "CRXCK"}, + "PA9": {"ERXERR", "SPI3_CS1", "", "GNULL", "I2S1_MCLK"}, + "PA10": {"ERXDV", "", "UART1_TX", "GRXCTL"}, + "PA11": {"EMDC", "", "UART1_RX", "GMDC"}, + "PA12": {"EMDIO", "UART6_TX", "UART1_RTS", "GMDIO"}, + "PA13": {"ETXEN", "UART6_RX", "UART1_CTS", "GTXCTL"}, + "PA14": {"ETXCK", "UART7_TX", "UART1_DTR", "GNULL", "I2S1_SCK"}, + "PA15": {"ECRS", "UART7_RX", "UART1_DSR", "GTXCK", "I2S1_WS"}, + "PA16": {"ECOL", "CAN_TX", "UART1_DCD", "GCLKIN", "I2S1_DOUT"}, + "PA17": {"ETXERR", "CAN_RX", "UART1_RI", "GNULL", "I2S1_DIN"}, + "PB0": {"I2C0_SCL"}, + "PB1": {"I2C0_SDA"}, + "PB2": {"PWM0"}, + "PB3": {"IR0_TX", "", "SPDIF_MCLK", "", "STANBYWFI"}, + "PB4": {"IR0_RX"}, + "PB5": {"I2S0_MCLK", "AC97_MCLK"}, + "PB6": {"I2S0_SCK", "AC97_SCK"}, + "PB7": {"I2S0_WS", "AC97_SYNC"}, + "PB8": {"I2S0_DOUT0", "AC97_DOUT"}, + "PB9": {"I2S0_DOUT1"}, + "PB10": {"I2S0_DOUT2"}, + "PB11": {"I2S0_DOUT3"}, + "PB12": {"I2S0_DIN", "AC97_DI", "SPDIF_DI"}, + "PB13": {"SPI2_CS1", "", "SPDIF_DO"}, + "PB14": {"SPI2_CS0", "JTAG0_TMS"}, + "PB15": {"SPI2_CLK", "JTAG0_TCK"}, + "PB16": {"SPI2_MOSI", "JTAG0_TDO"}, + "PB17": {"SPI2_MISO", "JTAG0_TDI"}, + "PB18": {"I2C1_SCL"}, + "PB19": {"I2C1_SDA"}, + "PB20": {"I2C2_SCL"}, + "PB21": {"I2C2_SDA"}, + "PB22": {"UART0_TX", "IR1_TX"}, + "PB23": {"UART0_RX", "IR1_RX"}, + "PC0": {"NWE#", "SPI0_MOSI"}, + "PC1": {"NALE", "SPI0_MISO"}, + "PC2": {"NCLE", "SPI0_CLK"}, + "PC3": {"NCE1"}, + "PC4": {"NCE0"}, + "PC5": {"NRE#"}, + "PC6": {"NRB0", "SDC2_CMD"}, + "PC7": {"NRB1", "SDC2_CLK"}, + "PC8": {"NDQ0", "SDC2_D0"}, + "PC9": {"NDQ1", "SDC2_D1"}, + "PC10": {"NDQ2", "SDC2_D2"}, + "PC11": {"NDQ3", "SDC2_D3"}, + "PC12": {"NDQ4"}, + "PC13": {"NDQ5"}, + "PC14": {"NDQ6"}, + "PC15": {"NDQ7"}, + "PC16": {"NWP"}, + "PC17": {"NCE2"}, + "PC18": {"NCE3"}, + "PC19": {"NCE4", "SPI2_CS0", "", "", "PC_EINT12"}, + "PC20": {"NCE5", "SPI2_CLK", "", "", "PC_EINT13"}, + "PC21": {"NCE6", "SPI2_MOSI", "", "", "PC_EINT14"}, + "PC22": {"NCE7", "SPI2_MISO", "", "", "PC_EINT15"}, + "PC23": {"", "SPI2_CS0"}, + "PC24": {"NDQS"}, + "PD0": {"LCD0_D0", "LVDS0_VP0"}, + "PD1": {"LCD0_D1", "LVDS0_VN0"}, + "PD2": {"LCD0_D2", "LVDS0_VP1"}, + "PD3": {"LCD0_D3", "LVDS0_VN1"}, + "PD4": {"LCD0_D4", "LVDS0_VP2"}, + "PD5": {"LCD0_D5", "LVDS0_VN2"}, + "PD6": {"LCD0_D6", "LVDS0_VPC"}, + "PD7": {"LCD0_D7", "LVDS0_VNC"}, + "PD8": {"LCD0_D8", "LVDS0_VP3"}, + "PD9": {"LCD0_D9", "LVDS0_VN3"}, + "PD10": {"LCD0_D10", "LVDS1_VP0"}, + "PD11": {"LCD0_D11", "LVDS1_VN0"}, + "PD12": {"LCD0_D12", "LVDS1_VP1"}, + "PD13": {"LCD0_D13", "LVDS1_VN1"}, + "PD14": {"LCD0_D14", "LVDS1_VP2"}, + "PD15": {"LCD0_D15", "LVDS1_VN2"}, + "PD16": {"LCD0_D16", "LVDS1_VPC"}, + "PD17": {"LCD0_D17", "LVDS1_VNC"}, + "PD18": {"LCD0_D18", "LVDS1_VP3"}, + "PD19": {"LCD0_D19", "LVDS1_VN3"}, + "PD20": {"LCD0_D20", "CSI1_MCLK"}, + "PD21": {"LCD0_D21", "SMC_VPPEN"}, + "PD22": {"LCD0_D22", "SMC_VPPPP"}, + "PD23": {"LCD0_D23", "SMC_DET"}, + "PD24": {"LCD0_CLK", "SMC_VCCEN"}, + "PD25": {"LCD0_DE", "SMC_RST"}, + "PD26": {"LCD0_HSYNC", "SMC_SLK"}, + "PD27": {"LCD0_VSYNC", "SMC_SDA"}, + "PE0": {"TS0_CLK", "CSI0_PCLK"}, + "PE1": {"TS0_ERR", "CSI0_MCLK"}, + "PE2": {"TS0_SYNC", "CSI0_HSYNC"}, + "PE3": {"TS0_DLVD", "CSI0_VSYNC"}, + "PE4": {"TS0_D0", "CSI0_D0"}, + "PE5": {"TS0_D1", "CSI0_D1"}, + "PE6": {"TS0_D2", "CSI0_D2"}, + "PE7": {"TS0_D3", "CSI0_D3"}, + "PE8": {"TS0_D4", "CSI0_D4"}, + "PE9": {"TS0_D5", "CSI0_D5"}, + "PE10": {"TS0_D6", "CSI0_D6"}, + "PE11": {"TS0_D7", "CSI0_D7"}, + "PF0": {"SDC0_D1", "", "JTAG1_TMS"}, + "PF1": {"SDC0_D0", "", "JTAG1_TDI"}, + "PF2": {"SDC0_CLK", "", "UART0_TX"}, + "PF3": {"SDC0_CMD", "", "JTAG1_TDO"}, + "PF4": {"SDC0_D3", "", "UART0_RX"}, + "PF5": {"SDC0_D2", "", "JTAG1_TCK"}, + "PG0": {"TS1_CLK", "CSI1_PCLK", "SDC1_CMD"}, + "PG1": {"TS1_ERR", "CSI1_MCLK", "SDC1_CLK"}, + "PG2": {"TS1_SYNC", "CSI1_HSYNC", "SDC1_D0"}, + "PG3": {"TS1_DVLD", "CSI1_VSYNC", "SDC1_D1"}, + "PG4": {"TS1_D0", "CSI1_D0", "SDC1_D2", "CSI0_D8"}, + "PG5": {"TS1_D1", "CSI1_D1", "SDC1_D3", "CSI0_D9"}, + "PG6": {"TS1_D2", "CSI1_D2", "UART3_TX", "CSI0_D10"}, + "PG7": {"TS1_D3", "CSI1_D3", "UART3_RX", "CSI0_D11"}, + "PG8": {"TS1_D4", "CSI1_D4", "UART3_RTS", "CSI0_D12"}, + "PG9": {"TS1_D5", "CSI1_D4", "UART3_CTS", "CSI0_D13"}, + "PG10": {"TS1_D6", "CSI1_D6", "UART4_TX", "CSI0_D14"}, + "PG11": {"TS1_D7", "CSI1_D7", "UART4_RX", "CSI0_D15"}, + "PH0": {"LCD1_D0", "", "UART3_TX", "", "PH_EINT0"}, + "PH1": {"LCD1_D1", "", "UART3_RX", "", "PH_EINT1"}, + "PH2": {"LCD1_D2", "", "UART3_RTS", "", "PH_EINT2"}, + "PH3": {"LCD1_D3", "", "UART3_CTS", "", "PH_EINT3"}, + "PH4": {"LCD1_D4", "", "UART4_TX", "", "PH_EINT4"}, + "PH5": {"LCD1_D5", "", "UART4_RX", "", "PH_EINT5"}, + "PH6": {"LCD1_D6", "", "UART5_TX", "MS_BS", "PH_EINT6"}, + "PH7": {"LCD1_D7", "", "UART5_RX", "MS_CLK", "PH_EINT7"}, + "PH8": {"LCD1_D8", "ERXD3", "KP_IN0", "MS_D0", "PH_EINT8"}, + "PH9": {"LCD1_D9", "ERXD2", "KP_IN1", "MS_D1", "PH_EINT9"}, + "PH10": {"LCD1_D10", "ERXD1", "KP_IN2", "MS_D2", "PH_EINT10"}, + "PH11": {"LCD1_D11", "ERXD0", "KP_IN3", "MS_D3", "PH_EINT11"}, + "PH12": {"LCD1_D12", "", "PS2_SCK1", "", "PH_EINT12"}, + "PH13": {"LCD1_D13", "", "PS2_SDA1", "SMC_RST", "PH_EINT13"}, + "PH14": {"LCD1_D14", "ETXD3", "KP_IN4", "SMC_VPPEN", "PH_EINT14"}, + "PH15": {"LCD1_D15", "ETXD2", "KP_IN5", "SMC_VPPPP", "PH_EINT15"}, + "PH16": {"LCD1_D16", "ETXD1", "KP_IN6", "SMC_DET", "PH_EINT16"}, + "PH17": {"LCD1_D17", "ETXD0", "KP_IN7", "SMC_VCCEN", "PH_EINT17"}, + "PH18": {"LCD1_D18", "ERXCK", "KP_OUT0", "SMC_SLK", "PH_EINT18"}, + "PH19": {"LCD1_D19", "ERXERR", "KP_OUT1", "SMC_SDA", "PH_EINT19"}, + "PH20": {"LCD1_D20", "ERXDV", "CAN_TX", "", "PH_EINT20"}, + "PH21": {"LCD1_D21", "EMDC", "CAN_RX", "", "PH_EINT21"}, + "PH22": {"LCD1_D22", "EMDIO", "KP_OUT2", "SDC1_CMD", ""}, + "PH23": {"LCD1_D23", "ETXEN", "KP_OUT3", "SDC1_CLK", ""}, + "PH24": {"LCD1_CLK", "ETXCK", "KP_OUT4", "SDC1_D0", ""}, + "PH25": {"LCD1_DE", "ECRS", "KP_OUT5", "SDC1_D1", ""}, + "PH26": {"LCD1_HSYNC", "ECOL", "KP_OUT6", "SDC1_D2", ""}, + "PH27": {"LCD1_VSYNC", "ETXERR", "KP_OUT7", "SDC1_D3", ""}, + "PI0": {"", "I2C3_SCL"}, + "PI1": {"", "I2C3_SDA"}, + "PI2": {"", "I2C4_SCL"}, + "PI3": {"PWM1", "I2C4_SDA"}, + "PI4": {"SDC3_CMD"}, + "PI5": {"SDC3_CLK"}, + "PI6": {"SDC3_D0"}, + "PI7": {"SDC3_D1"}, + "PI8": {"SDC3_D2"}, + "PI9": {"SDC3_D3"}, + "PI10": {"SPI0_CS0", "UART5_TX", "", "PI_EINT22"}, + "PI11": {"SPI0_CLK", "UART5_RX", "", "PI_EINT23"}, + "PI12": {"SPI0_MOSI", "UART6_TX", "CLK_OUT_A", "PI_EINT24"}, + "PI13": {"SPI0_MISO", "UART6_RX", "CLK_OUT_B", "PI_EINT25"}, + "PI14": {"SPI0_CS0", "PS2_SCK1", "TCLKIN0", "PI_EINT26"}, + "PI15": {"SPI1_CS1", "PS2_SDA1", "TCLKIN1", "PI_EINT27"}, + "PI16": {"SPI1_CS0", "UART2_RTS", "", "PI_EINT28"}, + "PI17": {"SPI1_CLK", "UART2_CTS", "", "PI_EINT29"}, + "PI18": {"SPI1_MOSI", "UART2_TX", "", "PI_EINT30"}, + "PI19": {"SPI1_MISO", "UART2_RX", "", "PI_EINT31"}, + "PI20": {"PS2_SCK0", "UART7_TX", "HSCL"}, + "PI21": {"PS2_SDA0", "UART7_RX", "HSDA"}, +} + +// mapA20Pins uses mappingA20 to actually set the altFunc fields of all gpio +// and mark them as available. +// +// It is called by the generic allwinner processor code if an A20 is detected. +func mapA20Pins() error { + for name, altFuncs := range mappingA20 { + pin := cpupins[name] + pin.altFunc = altFuncs + pin.available = true + if strings.Contains(string(altFuncs[4]), "_EINT") || + strings.Contains(string(altFuncs[3]), "_EINT") { + pin.supportEdge = true + } + + // Initializes the sysfs corresponding pin right away. + pin.sysfsPin = sysfs.Pins[pin.Number()] + } + return nil +} diff --git a/vendor/periph.io/x/periph/host/allwinner/a64.go b/vendor/periph.io/x/periph/host/allwinner/a64.go new file mode 100644 index 0000000..82a97f8 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/a64.go @@ -0,0 +1,174 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// This file contains pin mapping information that is specific to the Allwinner +// A64 model. + +package allwinner + +import ( + "strings" + + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/sysfs" +) + +// A64 specific pins. +var ( + X32KFOUT *pin.BasicPin // Clock output of 32Khz crystal + KEY_ADC *pin.BasicPin // 6 bits resolution ADC for key application; can work up to 250Hz conversion rate; reference voltage is 2.0V + EAROUTP *pin.BasicPin // Earpiece amplifier negative differential output + EAROUTN *pin.BasicPin // Earpiece amplifier positive differential output +) + +// + +func init() { + X32KFOUT = &pin.BasicPin{N: "X32KFOUT"} + // BUG(maruel): These need to be converted to an analog.PinIO implementation + // once analog support is implemented. + KEY_ADC = &pin.BasicPin{N: "KEY_ADC"} + EAROUTP = &pin.BasicPin{N: "EAROUTP"} + EAROUTN = &pin.BasicPin{N: "EAROUTN"} +} + +// mappingA64 describes the mapping of the A64 processor gpios to their +// alternate functions. +// +// It omits the in & out functions which are available on all gpio. +// +// The mapping comes from the datasheet page 23: +// http://files.pine64.org/doc/datasheet/pine64/A64_Datasheet_V1.1.pdf +// +// - The datasheet uses TWI instead of I2C but it is renamed here for +// consistency. +// - AIF is an audio interface, i.e. to connect to S/PDIF. +// - RGMII means Reduced gigabit media-independent interface. +// - SDC means SDCard? +// - NAND connects to a NAND flash controller. +// - CSI and CCI are for video capture. +var mappingA64 = map[string][5]pin.Func{ + "PB0": {"UART2_TX", "", "JTAG0_TMS", "", "PB_EINT0"}, + "PB1": {"UART2_RX", "", "JTAG0_TCK", "SIM_PWREN", "PB_EINT1"}, + "PB2": {"UART2_RTS", "", "JTAG0_TDO", "SIM_VPPEN", "PB_EINT2"}, + "PB3": {"UART2_CTS", "I2S0_MCLK", "JTAG0_TDI", "SIM_VPPPP", "PB_EINT3"}, + "PB4": {"AIF2_SYNC", "I2S0_WS", "", "SIM_CLK", "PB_EINT4"}, + "PB5": {"AIF2_BCLK", "I2S0_SCK", "", "SIM_DATA", "PB_EINT5"}, + "PB6": {"AIF2_DOUT", "I2S0_DOUT", "", "SIM_RST", "PB_EINT6"}, + "PB7": {"AIF2_DIN", "I2S0_DIN", "", "SIM_DET", "PB_EINT7"}, + "PB8": {"", "", "UART0_TX", "", "PB_EINT8"}, + "PB9": {"", "", "UART0_RX", "", "PB_EINT9"}, + "PC0": {"NAND_WE", "", "SPI0_MOSI"}, + "PC1": {"NAND_ALE", "SDC2_DS", "SPI0_MISO"}, + "PC2": {"NAND_CLE", "", "SPI0_CLK"}, + "PC3": {"NAND_CE1", "", "SPI0_CS0"}, + "PC4": {"NAND_CE0"}, + "PC5": {"NAND_RE", "SDC2_CLK"}, + "PC6": {"NAND_RB0", "SDC2_CMD"}, + "PC7": {"NAND_RB1"}, + "PC8": {"NAND_DQ0", "SDC2_D0"}, + "PC9": {"NAND_DQ1", "SDC2_D1"}, + "PC10": {"NAND_DQ2", "SDC2_D2"}, + "PC11": {"NAND_DQ3", "SDC2_D3"}, + "PC12": {"NAND_DQ4", "SDC2_D4"}, + "PC13": {"NAND_DQ5", "SDC2_D5"}, + "PC14": {"NAND_DQ6", "SDC2_D6"}, + "PC15": {"NAND_DQ7", "SDC2_D7"}, + "PC16": {"NAND_DQS", "SDC2_RST"}, + "PD0": {"LCD_D2", "UART3_TX", "SPI1_CS0", "CCIR_CLK"}, + "PD1": {"LCD_D3", "UART3_RX", "SPI1_CLK", "CCIR_DE"}, + "PD2": {"LCD_D4", "UART4_TX", "SPI1_MOSI", "CCIR_HSYNC"}, + "PD3": {"LCD_D5", "UART4_RX", "SPI1_MISO", "CCIR_VSYNC"}, + "PD4": {"LCD_D6", "UART4_RTS", "", "CCIR_D0"}, + "PD5": {"LCD_D7", "UART4_CTS", "", "CCIR_D1"}, + "PD6": {"LCD_D10", "", "", "CCIR_D2"}, + "PD7": {"LCD_D11", "", "", "CCIR_D3"}, + "PD8": {"LCD_D12", "", "RGMII_RXD3", "CCIR_D4"}, + "PD9": {"LCD_D13", "", "RGMII_RXD2", "CCIR_D5"}, + "PD10": {"LCD_D14", "", "RGMII_RXD1"}, + "PD11": {"LCD_D15", "", "RGMII_RXD0"}, + "PD12": {"LCD_D18", "LVDS_VP0", "RGMII_RXCK"}, + "PD13": {"LCD_D19", "LVDS_VN0", "RGMII_RXCT"}, + "PD14": {"LCD_D20", "LVDS_VP1", "RGMII_RXER"}, + "PD15": {"LCD_D21", "LVDS_VN1", "RGMII_TXD3", "CCIR_D6"}, + "PD16": {"LCD_D22", "LVDS_VP2", "RGMII_TXD2", "CCIR_D7"}, + "PD17": {"LCD_D23", "LVDS_VN2", "RGMII_TXD1"}, + "PD18": {"LCD_CLK", "LVDS_VPC", "RGMII_TXD0"}, + "PD19": {"LCD_DE", "LVDS_VNC", "RGMII_TXCK"}, + "PD20": {"LCD_HSYNC", "LVDS_VP3", "RGMII_TXCT"}, + "PD21": {"LCD_VSYNC", "LVDS_VN3", "RGMII_CLKI"}, + "PD22": {"PWM0", "", "MDC"}, + "PD23": {"", "", "MDIO"}, + "PD24": {""}, + "PE0": {"CSI_PCLK", "", "TS_CLK"}, + "PE1": {"CSI_MCLK", "", "TS_ERR"}, + "PE2": {"CSI_HSYNC", "", "TS_SYNC"}, + "PE3": {"CSI_VSYNC", "", "TS_DVLD"}, + "PE4": {"CSI_D0", "", "TS_D0"}, + "PE5": {"CSI_D1", "", "TS_D1"}, + "PE6": {"CSI_D2", "", "TS_D2"}, + "PE7": {"CSI_D3", "", "TS_D3"}, + "PE8": {"CSI_D4", "", "TS_D4"}, + "PE9": {"CSI_D5", "", "TS_D5"}, + "PE10": {"CSI_D6", "", "TS_D6"}, + "PE11": {"CSI_D7", "", "TS_D7"}, + "PE12": {"CSI_SCK"}, + "PE13": {"CSI_SDA"}, + "PE14": {"PLL_LOCK_DBG", "I2C2_SCL"}, + "PE15": {"", "I2C2_SDA"}, + "PE16": {""}, + "PE17": {""}, + "PF0": {"SDC0_D1", "JTAG1_TMS"}, + "PF1": {"SDC0_D0", "JTAG1_TDI"}, + "PF2": {"SDC0_CLK", "UART0_TX"}, + "PF3": {"SDC0_CMD", "JTAG1_TDO"}, + "PF4": {"SDC0_D3", "UART0_RX"}, + "PF5": {"SDC0_D2", "JTAG1_TCK"}, + "PF6": {""}, + "PG0": {"SDC1_CLK", "", "", "", "PG_EINT0"}, + "PG1": {"SDC1_CMD", "", "", "", "PG_EINT1"}, + "PG2": {"SDC1_D0", "", "", "", "PG_EINT2"}, + "PG3": {"SDC1_D1", "", "", "", "PG_EINT3"}, + "PG4": {"SDC1_D2", "", "", "", "PG_EINT4"}, + "PG5": {"SDC1_D3", "", "", "", "PG_EINT5"}, + "PG6": {"UART1_TX", "", "", "", "PG_EINT6"}, + "PG7": {"UART1_RX", "", "", "", "PG_EINT7"}, + "PG8": {"UART1_RTS", "", "", "", "PG_EINT8"}, + "PG9": {"UART1_CTS", "", "", "", "PG_EINT9"}, + "PG10": {"AIF3_SYNC", "I2S1_WS", "", "", "PG_EINT10"}, + "PG11": {"AIF3_BCLK", "I2S1_SCK", "", "", "PG_EINT11"}, + "PG12": {"AIF3_DOUT", "I2S1_DOUT", "", "", "PG_EINT12"}, + "PG13": {"AIF3_DIN", "I2S1_DIN", "", "", "PG_EINT13"}, + "PH0": {"I2C0_SCL", "", "", "", "PH_EINT0"}, + "PH1": {"I2C0_SDA", "", "", "", "PH_EINT1"}, + "PH2": {"I2C1_SCL", "", "", "", "PH_EINT2"}, + "PH3": {"I2C1_SDA", "", "", "", "PH_EINT3"}, + "PH4": {"UART3_TX", "", "", "", "PH_EINT4"}, + "PH5": {"UART3_RX", "", "", "", "PH_EINT5"}, + "PH6": {"UART3_RTS", "", "", "", "PH_EINT6"}, + "PH7": {"UART3_CTS", "", "", "", "PH_EINT7"}, + "PH8": {"OWA_OUT", "", "", "", "PH_EINT8"}, + "PH9": {"", "", "", "", "PH_EINT9"}, + "PH10": {"MIC_CLK", "", "", "", "PH_EINT10"}, + "PH11": {"MIC_DATA", "", "", "", "PH_EINT11"}, +} + +// mapA64Pins uses mappingA64 to actually set the altFunc fields of all gpio +// and mark them as available. +// +// It is called by the generic allwinner processor code if an A64 is detected. +func mapA64Pins() error { + for name, altFuncs := range mappingA64 { + pin := cpupins[name] + pin.altFunc = altFuncs + pin.available = true + if strings.Contains(string(altFuncs[4]), "_EINT") { + pin.supportEdge = true + } + + // Initializes the sysfs corresponding pin right away. + pin.sysfsPin = sysfs.Pins[pin.Number()] + } + return nil +} diff --git a/vendor/periph.io/x/periph/host/allwinner/allwinner_arm.go b/vendor/periph.io/x/periph/host/allwinner/allwinner_arm.go new file mode 100644 index 0000000..b901423 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/allwinner_arm.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/allwinner/allwinner_arm64.go b/vendor/periph.io/x/periph/host/allwinner/allwinner_arm64.go new file mode 100644 index 0000000..1fb5b5f --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/allwinner_arm64.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build arm64 + +package allwinner + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/allwinner/allwinner_other.go b/vendor/periph.io/x/periph/host/allwinner/allwinner_other.go new file mode 100644 index 0000000..4795c11 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/allwinner_other.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm,!arm64 + +package allwinner + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/allwinner/clock.go b/vendor/periph.io/x/periph/host/allwinner/clock.go new file mode 100644 index 0000000..f615fde --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/clock.go @@ -0,0 +1,281 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +const ( + clockSPIEnable clockSPI = 1 << 31 // SCLK_GATING + // 30:26 reserved + clockSPIOSC24M clockSPI = 0 << 24 // CLK_SRC_SEL + clockSPIPLL6 clockSPI = 1 << 24 // A64: PLL_PERIPH0(1X) + clockSPIPLL5 clockSPI = 2 << 24 // A64: PLL_PERIPH1(1X) R8: PLL5 = DDR + // 23:18 reserved + clockSPIDiv1a clockSPI = 0 << 16 // CLK_DIV_RATIO_N + clockSPIDiv2a clockSPI = 1 << 16 // + clockSPIDiv4a clockSPI = 2 << 16 // + clockSPIDiv8a clockSPI = 3 << 16 // + // 15:4 reserved + clockSPIDiv1b clockSPI = 0 << 0 // CLK_DIV_RATIO_M + clockSPIDiv2b clockSPI = 1 << 0 // + clockSPIDiv3b clockSPI = 2 << 0 // + clockSPIDiv4b clockSPI = 3 << 0 // + clockSPIDiv5b clockSPI = 4 << 0 // + clockSPIDiv6b clockSPI = 5 << 0 // + clockSPIDiv7b clockSPI = 6 << 0 // + clockSPIDiv8b clockSPI = 7 << 0 // + clockSPIDiv9b clockSPI = 8 << 0 // + clockSPIDiv10b clockSPI = 9 << 0 // + clockSPIDiv11b clockSPI = 10 << 0 // + clockSPIDiv12b clockSPI = 11 << 0 // + clockSPIDiv13b clockSPI = 12 << 0 // + clockSPIDiv14b clockSPI = 13 << 0 // + clockSPIDiv15b clockSPI = 14 << 0 // + clockSPIDiv16b clockSPI = 15 << 0 // +) + +// Also valid for IR. +// +// SPI0_SCLK_CFG_REG / SPI1_SCLK_CFG_REG / SPI2_SCLK_CFG_REG / IR_SCLK_CFG_REG +// +// A64: Page 110-111. (Also Page 554?) +// R8: Page 71. +type clockSPI uint32 + +const ( + clockPLL6Enable clockPLL6R8Ctl = 1 << 31 // PLL6_Enable + clockPLL6Force24Mhz clockPLL6R8Ctl = 1 << 30 // PLL6_BYPASS_EN; force 24Mhz + // 29:13 reserved + clockPLL6FactorMulN0 clockPLL6R8Ctl = 0 << 8 // PLL6_FACTOR_N + clockPLL6FactorMulN1 clockPLL6R8Ctl = 1 << 8 // + clockPLL6FactorMulN2 clockPLL6R8Ctl = 2 << 8 // + clockPLL6FactorMulN3 clockPLL6R8Ctl = 3 << 8 // + clockPLL6FactorMulN4 clockPLL6R8Ctl = 4 << 8 // + clockPLL6FactorMulN5 clockPLL6R8Ctl = 5 << 8 // + clockPLL6FactorMulN6 clockPLL6R8Ctl = 6 << 8 // + clockPLL6FactorMulN7 clockPLL6R8Ctl = 7 << 8 // + clockPLL6FactorMulN8 clockPLL6R8Ctl = 8 << 8 // + clockPLL6FactorMulN9 clockPLL6R8Ctl = 9 << 8 // + clockPLL6FactorMulN10 clockPLL6R8Ctl = 10 << 8 // + clockPLL6FactorMulN11 clockPLL6R8Ctl = 11 << 8 // + clockPLL6FactorMulN12 clockPLL6R8Ctl = 12 << 8 // + clockPLL6FactorMulN13 clockPLL6R8Ctl = 13 << 8 // + clockPLL6FactorMulN14 clockPLL6R8Ctl = 14 << 8 // + clockPLL6FactorMulN15 clockPLL6R8Ctl = 15 << 8 // + clockPLL6FactorMulN16 clockPLL6R8Ctl = 16 << 8 // + clockPLL6FactorMulN17 clockPLL6R8Ctl = 17 << 8 // + clockPLL6FactorMulN18 clockPLL6R8Ctl = 18 << 8 // + clockPLL6FactorMulN19 clockPLL6R8Ctl = 19 << 8 // + clockPLL6FactorMulN20 clockPLL6R8Ctl = 20 << 8 // + clockPLL6FactorMulN21 clockPLL6R8Ctl = 21 << 8 // + clockPLL6FactorMulN22 clockPLL6R8Ctl = 22 << 8 // + clockPLL6FactorMulN23 clockPLL6R8Ctl = 23 << 8 // + clockPLL6FactorMulN24 clockPLL6R8Ctl = 24 << 8 // + clockPLL6FactorMulN25 clockPLL6R8Ctl = 25 << 8 // + clockPLL6FactorMulN26 clockPLL6R8Ctl = 26 << 8 // + clockPLL6FactorMulN27 clockPLL6R8Ctl = 27 << 8 // + clockPLL6FactorMulN28 clockPLL6R8Ctl = 28 << 8 // + clockPLL6FactorMulN29 clockPLL6R8Ctl = 29 << 8 // + clockPLL6FactorMulN30 clockPLL6R8Ctl = 30 << 8 // + clockPLL6FactorMulN31 clockPLL6R8Ctl = 31 << 8 // + clockPLL6Damping clockPLL6R8Ctl = 2 << 6 // + clockPLL6FactorMulK1 clockPLL6R8Ctl = 0 << 4 // PLL6_FACTOR_K + clockPLL6FactorMulK2 clockPLL6R8Ctl = 1 << 4 // + clockPLL6FactorMulK3 clockPLL6R8Ctl = 2 << 4 // + clockPLL6FactorMulK4 clockPLL6R8Ctl = 3 << 4 // + // 3:2 reserved + clockPLL6FactorDivM1 clockPLL6R8Ctl = 0 << 4 // PLL6_FACTOR_M + clockPLL6FactorDivM2 clockPLL6R8Ctl = 1 << 4 // + clockPLL6FactorDivM3 clockPLL6R8Ctl = 2 << 4 // + clockPLL6FactorDivM4 clockPLL6R8Ctl = 3 << 4 // +) + +// PLL6_CFG_REG +// R8: Page 63; default 0x21009931 +// +// Output = (24MHz*N*K)/M/2 +// Note: the output 24MHz*N*K clock must be in the range of 240MHz~3GHz if the +// bypass is disabled. +type clockPLL6R8Ctl uint32 + +// clockMap is the mapping of important registers across CPUs. +type clockMap struct { + reserved0 [0xA0 / 4]uint32 // + spi0Clk clockSPI // 0x0A0 SPI0_SCLK_CFG_REG SPI0 Clock + spi1Clk clockSPI // 0x0A4 SPI1_SCLK_CFG_REG SPI1 Clock + spi2Clk clockSPI // 0x0A8 SPI2_SCLK_CFG_REG SPI2 Clock (Not on A64) +} + +// R8: Page 57-59. +type clockMapR8 struct { + r0 uint32 // 0x000 PLL1_CFG_REG PLL1 Control + r1 uint32 // 0x004 PLL1_TUN_REG PLL1 Tuning + r2 uint32 // 0x008 PLL2_CFG_REG PLL2 Control + r3 uint32 // 0x00C PLL2_TUN_REG PLL2 Tuning + r4 uint32 // 0x010 PLL3_CFG_REG PLL3 Control + r5 uint32 // 0x014 + r6 uint32 // 0x018 PLL4_CFG_REG PLL4 Control + r7 uint32 // 0x01C + r8 uint32 // 0x020 PLL5_CFG_REG PLL5 Control + r9 uint32 // 0x024 PLL5_TUN_REG PLL5 Tuning + r10 clockPLL6R8Ctl // 0x028 PLL6_CFG_REG PLL6 Control + r11 uint32 // 0x02C PLL6 Tuning + r12 uint32 // 0x030 PLL7_CFG_REG + r13 uint32 // 0x034 + r14 uint32 // 0x038 PLL1_TUN2_REG PLL1 Tuning2 + r15 uint32 // 0x03C PLL5_TUN2_REG PLL5 Tuning2 + r16 uint32 // 0x04C + r17 uint32 // 0x050 OSC24M_CFG_REG OSC24M control + r18 uint32 // 0x054 CPU_AHB_APB0_CFG_REG CPU, AHB And APB0 Divide Ratio + r19 uint32 // 0x058 APB1_CLK_DIV_REG APB1 Clock Divider + r20 uint32 // 0x05C AXI_GATING_REG AXI Module Clock Gating + r21 uint32 // 0x060 AHB_GATING_REG0 AHB Module Clock Gating 0 + r22 uint32 // 0x064 AHB_GATING_REG1 AHB Module Clock Gating 1 + r23 uint32 // 0x068 APB0_GATING_REG APB0 Module Clock Gating + r24 uint32 // 0x06C APB1_GATING_REG APB1 Module Clock Gating + r25 uint32 // 0x080 NAND_SCLK_CFG_REG Nand Flash Clock + r26 uint32 // 0x084 + r27 uint32 // 0x088 SD0_SCLK_CFG_REG SD0 Clock + r28 uint32 // 0x08C SD1_SCLK_CFG_REG SD1 Clock + r29 uint32 // 0x090 SD2_SCLK_CFG_REG SD2 Clock + r30 uint32 // 0x094 + r31 uint32 // 0x098 + r32 uint32 // 0x09C CE_SCLK_CFG_REG Crypto Engine Clock + spi0Clk clockSPI // 0x0A0 SPI0_SCLK_CFG_REG SPI0 Clock + spi1Clk clockSPI // 0x0A4 SPI1_SCLK_CFG_REG SPI1 Clock + spi2Clk clockSPI // 0x0A8 SPI2_SCLK_CFG_REG SPI2 Clock + r33 uint32 // 0x0AC + irClk clockSPI // 0x0B0 IR_SCLK_CFG_REG IR Clock + r34 uint32 // 0x0B4 + r35 uint32 // 0x0B8 + r36 uint32 // 0x0BC + r37 uint32 // 0x0C0 + r38 uint32 // 0x0C4 + r39 uint32 // 0x0C8 + r40 uint32 // 0x0CC + r41 uint32 // 0x0D0 + r42 uint32 // 0x0D4 + r43 uint32 // 0x100 DRAM_SCLK_CFG_REG DRAM Clock + r44 uint32 // 0x104 BE_CFG_REG Display Engine Backend Clock + r45 uint32 // 0x108 + r46 uint32 // 0x10C FE_CFG_REG Display Engine Front End Clock + r47 uint32 // 0x110 + r48 uint32 // 0x114 + r49 uint32 // 0x118 + r50 uint32 // 0x11C + r51 uint32 // 0x120 + r52 uint32 // 0x124 + r53 uint32 // 0x128 + r54 uint32 // 0x12C LCD_CH1_CFG_REG LCD Channel1 Clock + r55 uint32 // 0x130 + r56 uint32 // 0x134 CSI_CFG_REG CSI Clock + r57 uint32 // 0x138 + r58 uint32 // 0x13C VE_CFG_REG Video Engine Clock + r59 uint32 // 0x140 AUDIO_CODEC_SCLK_CFG_REG Audio Codec Gating Special Clock + r60 uint32 // 0x144 AVS_SCLK_CFG_REG AVS Gating Special Clock + r61 uint32 // 0x148 + r62 uint32 // 0x14C + r63 uint32 // 0x150 + r64 uint32 // 0x154 MALI_CLOCK_CFG_REG Mali400 Gating Special Clock + r65 uint32 // 0x158 + r66 uint32 // 0x15C MBUS_SCLK_CFG_REG MBUS Gating Clock + r67 uint32 // 0x160 IEP_SCLK_CFG_REG IEP Gating Clock +} + +// A64: Page 81-84. +type clockMapA64 struct { + r0 uint32 // 0x000 PLL_CPUX_CTRL_REG PLL_CPUX Control Register + r1 uint32 // 0x008 PLL_AUDIO_CTRL_REG PLL_AUDIO Control Register + r2 uint32 // 0x010 PLL_VIDEO0_CTRL_REG PLL_VIDEO0 Control Register + r3 uint32 // 0x018 PLL_VE_CTRL_REG PLL_VE Control Register + r4 uint32 // 0x020 PLL_DDR0_CTRL_REG PLL_DDR0 Control Register + r5 uint32 // 0x028 PLL_PERIPH0_CTRL_REG PLL_PERIPH0 Control Register + r6 uint32 // 0x02C PLL_PERIPH1_CTRL_REG PLL_PERIPH1 Control Register + r7 uint32 // 0x030 PLL_VIDEO1_CTRL_REG PLL_VIDEO1 Control Register + r8 uint32 // 0x038 PLL_GPU_CTRL_REG PLL_GPU Control Register + r9 uint32 // 0x040 PLL_MIPI_CTRL_REG PLL_MIPI Control Register + r10 uint32 // 0x044 PLL_HSIC_CTRL_REG PLL_HSIC Control Register + r11 uint32 // 0x048 PLL_DE_CTRL_REG PLL_DE Control Register + r12 uint32 // 0x04C PLL_DDR1_CTRL_REG PLL_DDR1 Control Register + r13 uint32 // 0x050 CPU_AXI_CFG_REG CPUX/AXI Configuration Register + r14 uint32 // 0x054 AHB1_APB1_CFG_REG AHB1/APB1 Configuration Register + r15 uint32 // 0x058 APB2_CFG_REG APB2 Configuration Register + r16 uint32 // 0x05C AHB2_CFG_REG AHB2 Configuration Register + r17 uint32 // 0x060 BUS_CLK_GATING_REG0 Bus Clock Gating Register 0 + r18 uint32 // 0x064 BUS_CLK_GATING_REG1 Bus Clock Gating Register 1 + r19 uint32 // 0x068 BUS_CLK_GATING_REG2 Bus Clock Gating Register 2 + r20 uint32 // 0x06C BUS_CLK_GATING_REG3 Bus Clock Gating Register 3 + r21 uint32 // 0x070 BUS_CLK_GATING_REG4 Bus Clock Gating Register 4 + r22 uint32 // 0x074 THS_CLK_REG THS Clock Register + r23 uint32 // 0x080 NAND_CLK_REG NAND Clock Register + r24 uint32 // 0x088 SDMMC0_CLK_REG SDMMC0 Clock Register + r25 uint32 // 0x08C SDMMC1_CLK_REG SDMMC1 Clock Register + r26 uint32 // 0x090 SDMMC2_CLK_REG SDMMC2 Clock Register + r27 uint32 // 0x098 TS_CLK_REG TS Clock Register + r28 uint32 // 0x09C CE_CLK_REG CE Clock Register + spi0Clk clockSPI // 0x0A0 SPI0_CLK_REG SPI0 Clock Register + spi1Clk clockSPI // 0x0A4 SPI1_CLK_REG SPI1 Clock Register + r29 uint32 // 0x0B0 I2S/PCM-0_CLK_REG I2S/PCM-0 Clock Register + r30 uint32 // 0x0B4 I2S/PCM-1_CLK_REG I2S/PCM-1 Clock Register + r31 uint32 // 0x0B8 I2S/PCM-2_CLK_REG I2S/PCM-2 Clock Register + r32 uint32 // 0x0C0 SPDIF_CLK_REG SPDIF Clock Register + r33 uint32 // 0x0CC USBPHY_CFG_REG USBPHY Configuration Register + r34 uint32 // 0x0F4 DRAM_CFG_REG DRAM Configuration Register + r35 uint32 // 0x0F8 PLL_DDR_CFG_REG PLL_DDR Configuration Register + r36 uint32 // 0x0FC MBUS_RST_REG MBUS Reset Register + r37 uint32 // 0x100 DRAM_CLK_GATING_REG DRAM Clock Gating Register + r38 uint32 // 0x104 DE_CLK_REG DE Clock Register + r39 uint32 // 0x118 TCON0_CLK_REG TCON0 Clock Register + r40 uint32 // 0x11C TCON1_CLK_REG TCON1 Clock Register + r41 uint32 // 0x124 DEINTERLACE_CLK_REG DEINTERLACE Clock Register + r42 uint32 // 0x130 CSI_MISC_CLK_REG CSI_MISC Clock Register + r43 uint32 // 0x134 CSI_CLK_REG CSI Clock Register + r44 uint32 // 0x13C VE_CLK_REG VE Clock Register + r45 uint32 // 0x140 AC_DIG_CLK_REG AC Digital Clock Register + r46 uint32 // 0x144 AVS_CLK_REG AVS Clock Register + r47 uint32 // 0x150 HDMI_CLK_REG HDMI Clock Register + r48 uint32 // 0x154 HDMI_SLOW_CLK_REG HDMI Slow Clock Register + r49 uint32 // 0x15C MBUS_CLK_REG MBUS Clock Register + r50 uint32 // 0x168 MIPI_DSI_CLK_REG MIPI_DSI Clock Register + r51 uint32 // 0x1A0 GPU_CLK_REG GPU Clock Register + r52 uint32 // 0x200 PLL_STABLE_TIME_REG0 PLL Stable Time Register0 + r53 uint32 // 0x204 PLL_STABLE_TIME_REG1 PLL Stable Time Register1 + r54 uint32 // 0x21C PLL_PERIPH1_BIAS_REG PLL_PERIPH1 Bias Register + r55 uint32 // 0x220 PLL_CPUX_BIAS_REG PLL_CPUX Bias Register + r56 uint32 // 0x224 PLL_AUDIO_BIAS_REG PLL_AUDIO Bias Register + r57 uint32 // 0x228 PLL_VIDEO0_BIAS_REG PLL_VIDEO0 Bias Register + r58 uint32 // 0x22C PLL_VE_BIAS_REG PLL_VE Bias Register + r59 uint32 // 0x230 PLL_DDR0_BIAS_REG PLL_DDR0 Bias Register + r60 uint32 // 0x234 PLL_PERIPH0_BIAS_REG PLL_PERIPH0 Bias Register + r61 uint32 // 0x238 PLL_VIDEO1_BIAS_REG PLL_VIDEO1 Bias Register + r62 uint32 // 0x23C PLL_GPU_BIAS_REG PLL_GPU Bias Register + r63 uint32 // 0x240 PLL_MIPI_BIAS_REG PLL_MIPI Bias Register + r64 uint32 // 0x244 PLL_HSIC_BIAS_REG PLL_HSIC Bias Register + r65 uint32 // 0x248 PLL_DE_BIAS_REG PLL_DE Bias Register + r66 uint32 // 0x24C PLL_DDR1_BIAS_REG PLL_DDR1 Bias Register + r67 uint32 // 0x250 PLL_CPUX_TUN_REG PLL_CPUX Tuning Register + r68 uint32 // 0x260 PLL_DDR0_TUN_REG PLL_DDR0 Tuning Register + r69 uint32 // 0x270 PLL_MIPI_TUN_REG PLL_MIPI Tuning Register + r70 uint32 // 0x27C PLL_PERIPH1_PAT_CTRL_REG PLL_PERIPH1 Pattern Control Register + r71 uint32 // 0x280 PLL_CPUX_PAT_CTRL_REG PLL_CPUX Pattern Control Register + r72 uint32 // 0x284 PLL_AUDIO_PAT_CTRL_REG PLL_AUDIO Pattern Control Register + r73 uint32 // 0x288 PLL_VIDEO0_PAT_CTRL_REG PLL_VIDEO0 Pattern Control Register + r74 uint32 // 0x28C PLL_VE_PAT_CTRL_REG PLL_VE Pattern Control Register + r75 uint32 // 0x290 PLL_DDR0_PAT_CTRL_REG PLL_DDR0 Pattern Control Register + r76 uint32 // 0x298 PLL_VIDEO1_PAT_CTRL_REG PLL_VIDEO1 Pattern Control Register + r77 uint32 // 0x29C PLL_GPU_PAT_CTRL_REG PLL_GPU Pattern Control Register + r78 uint32 // 0x2A0 PLL_MIPI_PAT_CTRL_REG PLL_MIPI Pattern Control Register + r79 uint32 // 0x2A4 PLL_HSIC_PAT_CTRL_REG PLL_HSIC Pattern Control Register + r80 uint32 // 0x2A8 PLL_DE_PAT_CTRL_REG PLL_DE Pattern Control Register + r81 uint32 // 0x2AC PLL_DDR1_PAT_CTRL_REG0 PLL_DDR1 Pattern Control Register0 + r82 uint32 // 0x2B0 PLL_DDR1_PAT_CTRL_REG1 PLL_DDR1 Pattern Control Register1 + r83 uint32 // 0x2C0 BUS_SOFT_RST_REG0 Bus Software Reset Register 0 + r84 uint32 // 0x2C4 BUS_SOFT_RST_REG1 Bus Software Reset Register 1 + r85 uint32 // 0x2C8 BUS_SOFT_RST_REG2 Bus Software Reset Register 2 + r86 uint32 // 0x2D0 BUS_SOFT_RST_REG3 Bus Software Reset Register 3 + r87 uint32 // 0x2D8 BUS_SOFT_RST_REG4 Bus Software Reset Register 4 + r88 uint32 // 0x2F0 CCM_SEC_SWITCH_REG CCM Security Switch Register + r89 uint32 // 0x300 PS_CTRL_REG PS Control Register + r90 uint32 // 0x304 PS_CNT_REG PS Counter Register + r91 uint32 // 0x320 PLL_LOCK_CTRL_REG PLL Lock Control Register +} diff --git a/vendor/periph.io/x/periph/host/allwinner/detect.go b/vendor/periph.io/x/periph/host/allwinner/detect.go new file mode 100644 index 0000000..6235ee0 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/detect.go @@ -0,0 +1,98 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +import ( + "strings" + "sync" + + "periph.io/x/periph/host/distro" +) + +// Present detects whether the host CPU is an Allwinner CPU. +// +// https://en.wikipedia.org/wiki/Allwinner_Technology +func Present() bool { + detection.do() + return detection.isAllwinner +} + +// IsR8 detects whether the host CPU is an Allwinner R8 CPU. +// +// It looks for the string "sun5i-r8" in /proc/device-tree/compatible. +func IsR8() bool { + detection.do() + return detection.isR8 +} + +// IsA20 detects whether the host CPU is an Allwinner A20 CPU. +// +// It first looks for the string "sun71-a20" in /proc/device-tree/compatible, +// and if that fails it checks for "Hardware : sun7i" in /proc/cpuinfo. +func IsA20() bool { + detection.do() + return detection.isA20 +} + +// IsA64 detects whether the host CPU is an Allwinner A64 CPU. +// +// It looks for the string "sun50iw1p1" in /proc/device-tree/compatible. +func IsA64() bool { + detection.do() + return detection.isA64 +} + +// + +type detectionS struct { + mu sync.Mutex + done bool + isAllwinner bool + isR8 bool + isA20 bool + isA64 bool +} + +var detection detectionS + +// do contains the CPU detection logic that determines whether we have an +// Allwinner CPU and if so, which exact model. +// +// Sadly there is no science behind this, it's more of a trial and error using +// as many boards and OS flavors as possible. +func (d *detectionS) do() { + d.mu.Lock() + defer d.mu.Unlock() + if !d.done { + d.done = true + if isArm { + for _, c := range distro.DTCompatible() { + if strings.Contains(c, "sun50iw1p1") { + d.isA64 = true + } + if strings.Contains(c, "sun5i-r8") { + d.isR8 = true + } + if strings.Contains(c, "sun7i-a20") { + d.isA20 = true + } + } + d.isAllwinner = d.isA64 || d.isR8 || d.isA20 + + if !d.isAllwinner { + // The kernel in the image that comes pre-installed on the pcDuino3 Nano + // is an old 3.x kernel that doesn't expose the device-tree in procfs, + // so do an extra check in cpuinfo as well if we haven't detected + // anything yet. + // Distros based on 4.x kernels do expose it. + if hw, ok := distro.CPUInfo()["Hardware"]; ok { + if hw == "sun7i" { + d.isA20 = true + } + } + } + } + } +} diff --git a/vendor/periph.io/x/periph/host/allwinner/dma.go b/vendor/periph.io/x/periph/host/allwinner/dma.go new file mode 100644 index 0000000..6a608b8 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/dma.go @@ -0,0 +1,474 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Unlike the bcm283x, the allwinner CPUs do not have a "clear bit" and "set +// bit" registers, they only have the data register. Also, allwinner CPUs do +// not support linked lists of DMA buffers. On the other hand, the Allwinner DMA +// controller supports 8 bits transfers instead of 32-128 bits that the bcm283x +// DMA controller supports. +// +// This means that only 8 bits can be used per sample, and only one stream is +// necessary. This results in 1/8th th memory usage than on the bcm283x. The +// drawback is that a block of 8 contiguous GPIO pins must be dedicated to the +// stream. + +package allwinner + +import ( + "errors" + "fmt" + "log" + "os" + + "periph.io/x/periph" + "periph.io/x/periph/host/pmem" +) + +// dmaMap represents the DMA memory mapped CPU registers. +// +// This map is specific to the currently supported CPUs and will have to be +// adapted as more CPUs are supported. In particular the number of physical +// channels varies across different CPUs. +// +// Note that we modify the DMA controllers without telling the kernel driver. +// The driver keeps its own table of which DMA channel is available so this +// code could effectively crash the whole system. It practice this works. +// #everythingisfine +type dmaMap struct { + irqEn dmaR8Irq // DMA_IRQ_EN_REG + irqPendStas dmaR8PendingIrq // DMA_IRQ_PEND_STAS_REG + reserved0 [(0x100 - 8) / 4]uint32 // + normal [8]dmaR8NormalGroup // 0x100 The "8" "normal" DMA channels (only one active at a time so there's effectively one) + reserved1 [0x100 / 4]uint32 // + dedicated [8]dmaDedicatedGroup // 0x300 The 8 "dedicated" (as in actually existing) DMA channels +} + +func (d *dmaMap) getDedicated() int { + for i := len(d.dedicated) - 1; i >= 0; i-- { + if d.dedicated[i].isAvailable() { + return i + } + } + return -1 +} + +// dmaNormalGroup is the control registers for the first block of 8 DMA +// controllers. +// +// They can be intentionally slowed down, unlike the dedicated DMA ones. +// +// The big caveat is that only one controller can be active at a time and the +// execution sequence is in accordance with the priority level. This means that +// two normal DMA cannot be used to do simultaneous read and write. This +// feature is critical for bus bitbanging. +type dmaR8NormalGroup struct { + cfg ndmaR8Cfg // NDMA_CTRL_REG + srcAddr uint32 // NDMA_SRC_ADDR_REG + dstAddr uint32 // NDMA_DEST_ADDR_REG + byteCounter uint32 // NDMA_BC_REG + reserved [4]uint32 // +} + +func (d *dmaR8NormalGroup) isAvailable() bool { + return d.cfg == 0 && d.srcAddr == 0 && d.dstAddr == 0 && d.byteCounter == 0 +} + +func (d *dmaR8NormalGroup) release() error { + d.srcAddr = 0 + d.dstAddr = 0 + d.byteCounter = 0 + d.cfg = ndmaLoad + //drvDMA.dmaMemory.irqEn &^= ... + //drvDMA.dmaMemory.irqPendStas &^= ... + return nil +} + +// dmaNormalGroup is the control registers for the second block of 8 DMA +// controllers. +// +// They support different DReq and can do non-linear streaming. +type dmaDedicatedGroup struct { + cfg ddmaR8Cfg // DDMA_CTRL_REG + srcAddr uint32 // DDMA_SRC_ADDR_REG + dstAddr uint32 // DDMA_DEST_ADDR_REG + byteCounter uint32 // DDMA_BC_REG (24 bits) + reserved0 [2]uint32 // + param ddmaR8Param // DDMA_PARA_REG (dedicated DMA only) + reserved1 uint32 // +} + +func (d *dmaDedicatedGroup) isAvailable() bool { + return d.cfg == 0 && d.srcAddr == 0 && d.dstAddr == 0 && d.byteCounter == 0 && d.param == 0 +} + +func (d *dmaDedicatedGroup) set(srcAddr, dstAddr, l uint32, srcIO, dstIO bool, src ddmaR8Cfg) { + d.srcAddr = srcAddr + d.dstAddr = dstAddr + d.byteCounter = l + // TODO(maruel): Slow down the clock by another 2*250x + //d.param = ddmaR8Param(250 | 250<<16) + d.param = ddmaR8Param(1<<24 | 1<<8 | 1) + // All these have value 0. This statement only exist for documentation. + cfg := ddmaDstWidth8 | ddmaDstBurst1 | ddmaDstLinear | ddmaSrcWidth8 | ddmaSrcLinear | ddmaSrcBurst1 + cfg |= src | ddmaBCRemain + if srcIO { + cfg |= ddmaSrcIOMode + } else if dstIO { + cfg |= ddmaDstIOMode + } + d.cfg = ddmaLoad | cfg + for i := 0; d.cfg&ddmaLoad != 0 && i < 100000; i++ { + } + if d.cfg&ddmaLoad != 0 { + log.Printf("failed to load DDMA: %# v\n", d) + } +} + +func (d *dmaDedicatedGroup) release() error { + d.param = 0 + d.srcAddr = 0 + d.dstAddr = 0 + d.byteCounter = 0 + d.cfg = ddmaLoad + //drvDMA.dmaMemory.irqEn &^= ... + //drvDMA.dmaMemory.irqPendStas &^= ... + return nil +} + +const ( + // 31 reserved + dma7QueueEndIrq dmaA64Irq = 1 << 30 // DMA7_END_IRQ_EN; DMA 7 Queue End Transfer Interrupt Enable. + dma7PackageEndIrq dmaA64Irq = 1 << 29 // DMA7_PKG_IRQ_EN; DMA 7 Package End Transfer Interrupt Enable. + dma7HalfIrq dmaA64Irq = 1 << 28 // DMA7_HLAF_IRQ_EN; DMA 7 Half Package Transfer Interrupt Enable. + // ... + // 3 reserved + dma0QueueEndIrq dmaA64Irq = 1 << 2 // DMA0_END_IRQ_EN; DMA 0 Queue End Transfer Interrupt Enable. + dma0PackageEndIrq dmaA64Irq = 1 << 1 // DMA0_PKG_IRQ_EN; DMA 0 Package End Transfer Interrupt Enable. + dma0HalfIrq dmaA64Irq = 1 << 0 // DMA0_HLAF_IRQ_EN; DMA 0 Half Package Transfer Interrupt Enable. +) + +// DMA_IRQ_EN_REG +// A64: Page 199-201. +type dmaA64Irq uint32 + +const ( + ddma7EndIrq dmaR8Irq = 1 << 31 // DDMA7_END_IRQ_EN + ddma7HalfIreq dmaR8Irq = 1 << 30 // DDMA7_HF_IRQ_EN + // ... + ddma0EndIrq dmaR8Irq = 1 << 17 // DDMA0_END_IRQ_EN + ddma0HalfIreq dmaR8Irq = 1 << 16 // DDMA0_HF_IRQ_EN + ndma7EndIrq dmaR8Irq = 1 << 15 // NDMA7_END_IRQ_EN + ndma7HalfIreq dmaR8Irq = 1 << 16 // NDDMA7_HF_IRQ_EN + // ... + ndma0EndIrq dmaR8Irq = 1 << 1 // NDMA0_END_IRQ_EN + ndma0HFIreq dmaR8Irq = 1 << 0 // NDMA0_HF_IRQ_EN +) + +// DMA_IRQ_EN_REG +// R8: Page 124-126. +type dmaR8Irq uint32 + +const ( + // 31 reserved + dma7QueueEndIrqPend dmaA64PendingIrq = 1 << 30 // DMA7_QUEUE_IRQ_PEND; DMA 7 Queue End Transfer Interrupt Pending. Set 1 to the bit will clear it. + dma7PackageEndIrqPend dmaA64PendingIrq = 1 << 29 // DMA7_PKG_IRQ_PEND; DMA 7 Package End Transfer Interrupt Pending. Set 1 to the bit will clear it. + dma7HalfIrqPend dmaA64PendingIrq = 1 << 28 // DMA7_HLAF_IRQ_PEND; DMA 7 Half Package Transfer Interrupt Pending. Set 1 to the bit will clear it. + // ... + // 3 reserved + dma0QueueEndIrqPend dmaA64PendingIrq = 1 << 2 // DMA0_QUEUE_IRQ_PEND; DMA 0 Queue End Transfer Interrupt Pending. Set 1 to the bit will clear it. + dma0PackageEndIrqPend dmaA64PendingIrq = 1 << 1 // DMA0_PKG_IRQ_PEND; DMA 0 Package End Transfer Interrupt Pending. Set 1 to the bit will clear it. + dma0HalfIrqPend dmaA64PendingIrq = 1 << 0 // DMA0_HLAF_IRQ_PEND; DMA 0 Half Package Transfer Interrupt Pending. Set 1 to the bit will clear it. +) + +// DMA_IRQ_PEND_REG0 +// A64: Page 201-203. +type dmaA64PendingIrq uint32 + +const ( + ddma7EndIrqPend dmaR8PendingIrq = 1 << 31 // DDMA7_END_IRQ_PEND + ddma7HalfIreqPend dmaR8PendingIrq = 1 << 30 // DDMA7_HF_IRQ_PEND + // ... + ddma0EndIrqPend dmaR8PendingIrq = 1 << 17 // DDMA0_END_IRQ_PEND + ddma0HalfIreqPend dmaR8PendingIrq = 1 << 16 // DDMA0_HF_IRQ_PEND + ndma7EndIrqPend dmaR8PendingIrq = 1 << 15 // NDMA7_END_IRQ_PEND + ndma7HalfIreqPend dmaR8PendingIrq = 1 << 16 // NDDMA7_HF_IRQ_PEND + // ... + ndma0EndIrqPend dmaR8PendingIrq = 1 << 1 // NDMA0_END_IRQ_PEND + ndma0HalfIreqPend dmaR8PendingIrq = 1 << 0 // NDMA0_HF_IRQ_PEND +) + +// DMA_IRQ_PEND_STAS_REG +// R8: Page 126-129. +type dmaR8PendingIrq uint32 + +const ( + ndmaLoad ndmaR8Cfg = 1 << 31 // NDMA_LOAD + ndmaContinuous ndmaR8Cfg = 1 << 30 // NDMA_CONTI_EN Continuous mode + ndmaWaitClk0 ndmaR8Cfg = 0 << 27 // NDMA_WAIT_STATE Number of clock to wait for + ndmaWaitClk2 ndmaR8Cfg = 1 << 27 // 2(n+1) + ndmaWaitClk6 ndmaR8Cfg = 2 << 27 // + ndmaWaitClk8 ndmaR8Cfg = 3 << 27 // + ndmaWaitClk10 ndmaR8Cfg = 4 << 27 // + ndmaWaitClk12 ndmaR8Cfg = 5 << 27 // + ndmaWaitClk14 ndmaR8Cfg = 6 << 27 // + ndmaWaitClk16 ndmaR8Cfg = 7 << 27 // + ndmaDstWidth32 ndmaR8Cfg = 2 << 25 // NDMA_DST_DATA_WIDTH + ndmaDstWidth16 ndmaR8Cfg = 1 << 25 // + ndmaDstWidth8 ndmaR8Cfg = 0 << 25 // + ndmaDstBurst8 ndmaR8Cfg = 2 << 23 // NDMA_DST_BST_LEN + ndmaDstBurst4 ndmaR8Cfg = 1 << 23 // + ndmaDstBurst1 ndmaR8Cfg = 0 << 23 // + // 22 reserved NDMA_CFG_DST_NON_SECURE ? + ndmaDstAddrNoInc ndmaR8Cfg = 1 << 21 // NDMA_DST_ADDR_TYPE + ndmaDstDrqIRTX ndmaR8Cfg = 0 << 16 // NDMA_DST_DRQ_TYPE + ndmaDstDrqUART1TX ndmaR8Cfg = 9 << 16 // + ndmaDstDrqUART3TX ndmaR8Cfg = 11 << 16 // + ndmaDstDrqAudio ndmaR8Cfg = 19 << 16 // 24.576MHz (Page 53) + ndmaDstDrqSRAM ndmaR8Cfg = 21 << 16 // + ndmaDstDrqSPI0TX ndmaR8Cfg = 24 << 16 // + ndmaDstDrqSPI1TX ndmaR8Cfg = 25 << 16 // + ndmaDstDrqSPI2TX ndmaR8Cfg = 26 << 16 // + ndmaDstDrqUSB1 ndmaR8Cfg = 27 << 16 // 480MHz + ndmaDstDrqUSB2 ndmaR8Cfg = 28 << 16 // + ndmaDstDrqUSB3 ndmaR8Cfg = 29 << 16 // + ndmaDstDrqUSB4 ndmaR8Cfg = 30 << 16 // + ndmaDstDrqUSB5 ndmaR8Cfg = 31 << 16 // + ndmaBCRemain ndmaR8Cfg = 1 << 15 // BC_MODE_SEL + // 14:11 reserved + ndmaSrcWidth32 ndmaR8Cfg = 2 << 9 // NDMA_SRC_DATA_WIDTH + ndmaSrcWidth16 ndmaR8Cfg = 1 << 9 // + ndmaSrcWidth8 ndmaR8Cfg = 0 << 9 // + ndmaSrcBurst8 ndmaR8Cfg = 2 << 7 // NDMA_SRC_BST_LEN + ndmaSrcBurst4 ndmaR8Cfg = 1 << 7 // + ndmaSrcBurst1 ndmaR8Cfg = 0 << 7 // + // 6 reserved NDMA_CFG_SRC_NON_SECURE ? + ndmaSrcAddrNoInc ndmaR8Cfg = 1 << 5 // NDMA_SRC_ADDR_TYPE + ndmaSrcDrqIRTX ndmaR8Cfg = 0 << 0 // NDMA_SRC_DRQ_TYPE + ndmaSrcDrqUART1RX ndmaR8Cfg = 9 << 0 // + ndmaSrcDrqUART3RX ndmaR8Cfg = 11 << 0 // + ndmaSrcDrqAudio ndmaR8Cfg = 19 << 0 // 24.576MHz (Page 53) + ndmaSrcDrqSRAM ndmaR8Cfg = 21 << 0 // + ndmaSrcDrqSDRAM ndmaR8Cfg = 22 << 0 // 0~400MHz + ndmaSrcDrqTPAD ndmaR8Cfg = 23 << 0 // + ndmaSrcDrqSPI0RX ndmaR8Cfg = 24 << 0 // + ndmaSrcDrqSPI1RX ndmaR8Cfg = 25 << 0 // + ndmaSrcDrqSPI2RX ndmaR8Cfg = 26 << 0 // + ndmaSrcDrqUSB1 ndmaR8Cfg = 27 << 0 // 480MHz + ndmaSrcDrqUSB2 ndmaR8Cfg = 28 << 0 // + ndmaSrcDrqUSB3 ndmaR8Cfg = 29 << 0 // + ndmaSrcDrqUSB4 ndmaR8Cfg = 30 << 0 // + ndmaSrcDrqUSB5 ndmaR8Cfg = 31 << 0 // +) + +// NDMA_CTRL_REG +// R8: Page 129-131. +type ndmaR8Cfg uint32 + +const ( + ddmaLoad ddmaR8Cfg = 1 << 31 // DDMA_LOAD + ddmaBusy ddmaR8Cfg = 1 << 30 // DDMA_BSY_STA + ddmaContinuous ddmaR8Cfg = 1 << 29 // DDMA_CONTI_MODE_EN + // 28:27 reserved 28 = DDMA_CFG_DST_NON_SECURE ? + ddmaDstWidth32 ddmaR8Cfg = 2 << 25 // DDMA_DST_DATA_WIDTH + ddmaDstWidth16 ddmaR8Cfg = 1 << 25 // + ddmaDstWidth8 ddmaR8Cfg = 0 << 25 // + ddmaDstBurst8 ddmaR8Cfg = 2 << 23 // DDMA_DST_BST_LEN + ddmaDstBurst4 ddmaR8Cfg = 1 << 23 // + ddmaDstBurst1 ddmaR8Cfg = 0 << 23 // + ddmaDstVertical ddmaR8Cfg = 3 << 21 // DDMA_ADDR_MODE; no idea what it's use it. It's not explained in the datasheet ... + ddmaDstHorizontal ddmaR8Cfg = 2 << 21 // ... and the official drivers/dma/sun6i-dma.c driver doesn't use it + ddmaDstIOMode ddmaR8Cfg = 1 << 21 // Non incrementing + ddmaDstLinear ddmaR8Cfg = 0 << 21 // Normal incrementing position + ddmaDstDrqSRAM ddmaR8Cfg = 0 << 16 // DDMA_DST_DRQ_SEL + ddmaDstDrqSDRAM ddmaR8Cfg = 1 << 16 // DDR ram speed + ddmaDstDrqNAND ddmaR8Cfg = 3 << 16 // + ddmaDstDrqUSB0 ddmaR8Cfg = 4 << 16 // + ddmaDstDrqSPI1TX ddmaR8Cfg = 8 << 16 // + ddmaDstDrqCryptoTX ddmaR8Cfg = 10 << 16 // + ddmaDstDrqTCON0 ddmaR8Cfg = 14 << 16 // + ddmaDstDrqSPI0TX ddmaR8Cfg = 26 << 16 // + ddmaDstDrqSPI2TX ddmaR8Cfg = 28 << 16 // + ddmaBCRemain ddmaR8Cfg = 1 << 15 // BC_MODE_SEL + // 14:11 reserved + ddmaSrcWidth32 ddmaR8Cfg = 2 << 9 // DDMA_SRC_DATA_WIDTH + ddmaSrcWidth16 ddmaR8Cfg = 1 << 9 // + ddmaSrcWidth8 ddmaR8Cfg = 0 << 9 // + ddmaSrcBurst8 ddmaR8Cfg = 2 << 7 // DDMA_SRC_BST_LEN + ddmaSrcBurst4 ddmaR8Cfg = 1 << 7 // + ddmaSrcBurst1 ddmaR8Cfg = 0 << 7 // + ddmaSrcVertical ddmaR8Cfg = 3 << 5 // DDMA_SRC_ADDR_MODE + ddmaSrcHorizontal ddmaR8Cfg = 2 << 5 // + ddmaSrcIOMode ddmaR8Cfg = 1 << 5 // Non incrementing + ddmaSrcLinear ddmaR8Cfg = 0 << 5 // Normal incrementing position + // 4:0 drq + ddmaSrcDrqSRAM ddmaR8Cfg = 0 << 0 // DDMA_SRC_DRQ_TYPE + ddmaSrcDrqSDRAM ddmaR8Cfg = 1 << 0 // + ddmaSrcDrqNAND ddmaR8Cfg = 3 << 0 // + ddmaSrcDrqUSB0 ddmaR8Cfg = 4 << 0 // + ddmaSrcDrqSPI1RX ddmaR8Cfg = 9 << 0 // + ddmaSrcDrqCryptoRX ddmaR8Cfg = 11 << 0 // + ddmaSrcDrqSPI0RX ddmaR8Cfg = 27 << 0 // + ddmaSrcDrqSPI2RX ddmaR8Cfg = 29 << 0 // +) + +// DDMA_CFG_REG +// R8: Page 131-134. +type ddmaR8Cfg uint32 + +const ( + // For each value, N+1 is actually used. + ddmaDstBlkSizeMask ddmaR8Param = 0xFF << 24 // DEST_DATA_BLK_SIZE + ddmaDstWaitClkCycleMask ddmaR8Param = 0xFF << 16 // DEST_WAIT_CLK_CYC + ddmaSrcBlkSizeMask ddmaR8Param = 0xFF << 8 // SRC_DATA_BLK_SIZE + ddmaSrcWaitClkCycleMask ddmaR8Param = 0xFF << 0 // SRC_WAIT_CLK_CYC +) + +// DDMA_PARA_REG +// R8: Page 134. +type ddmaR8Param uint32 + +// smokeTest allocates two physical pages, ask the DMA controller to copy the +// data from one page to another (with a small offset) and make sure the +// content is as expected. +// +// This should take a fraction of a second and will make sure the driver is +// usable. +func smokeTest() error { + const size = 4096 // 4kb + const holeSize = 1 // Minimum DMA alignment. + + alloc := func(s int) (pmem.Mem, error) { + return pmem.Alloc(s) + } + + copyMem := func(pDst, pSrc uint64) error { + n := drvDMA.dmaMemory.getDedicated() + if n == -1 { + return errors.New("no channel available") + } + drvDMA.dmaMemory.irqEn &^= 3 << uint(2*n+16) + drvDMA.dmaMemory.irqPendStas = 3 << uint(2*n+16) + ch := &drvDMA.dmaMemory.dedicated[n] + defer func() { + _ = ch.release() + }() + ch.set(uint32(pSrc), uint32(pDst)+holeSize, 4096-2*holeSize, false, false, ddmaDstDrqSDRAM|ddmaSrcDrqSDRAM) + + for ch.cfg&ddmaBusy != 0 { + } + return nil + } + + return pmem.TestCopy(size, holeSize, alloc, copyMem) +} + +// driverDMA implements periph.Driver. +// +// It implements much more than the DMA controller, it also exposes the clocks, +// the PWM and PCM controllers. +type driverDMA struct { + // dmaMemory is the memory map of the CPU DMA registers. + dmaMemory *dmaMap + // pwmMemory is the memory map of the CPU PWM registers. + pwmMemory *pwmMap + // spiMemory is the memory mapping for the spi CPU registers. + spiMemory *spiMap + // clockMemory is the memory mapping for the clock CPU registers. + clockMemory *clockMap + // timerMemory is the memory mapping for the timer CPU registers. + timerMemory *timerMap +} + +func (d *driverDMA) String() string { + return "allwinner-dma" +} + +func (d *driverDMA) Prerequisites() []string { + return []string{"allwinner-gpio"} +} + +func (d *driverDMA) After() []string { + return nil +} + +func (d *driverDMA) Init() (bool, error) { + // dmaBaseAddr is the physical base address of the DMA registers. + var dmaBaseAddr uint32 + // pwmBaseAddr is the physical base address of the PWM registers. + var pwmBaseAddr uint32 + // spiBaseAddr is the physical base address of the clock registers. + var spiBaseAddr uint32 + // clockBaseAddr is the physical base address of the clock registers. + var clockBaseAddr uint32 + // timerBaseAddr is the physical base address of the timer registers. + var timerBaseAddr uint32 + if IsA64() { + // Page 198. + dmaBaseAddr = 0x1C02000 + // Page 194. + pwmBaseAddr = 0x1C21400 + // Page 161. + timerBaseAddr = 0x1C20C00 + // Page 81. + clockBaseAddr = 0x1C20000 + // Page Page 545. + spiBaseAddr = 0x01C68000 + } else if IsR8() { + // Page 124. + dmaBaseAddr = 0x1C02000 + // Page 83. + pwmBaseAddr = 0x1C20C00 + 0x200 + // Page 85. + timerBaseAddr = 0x1C20C00 + // Page 57. + clockBaseAddr = 0x1C20000 + // Page 151. + spiBaseAddr = 0x01C05000 + } else { + // H3 + // Page 194. + //dmaBaseAddr = 0x1C02000 + // Page 187. + //pwmBaseAddr = 0x1C21400 + // Page 154. + //timerBaseAddr = 0x1C20C00 + return false, errors.New("unsupported CPU architecture") + } + + if err := pmem.MapAsPOD(uint64(dmaBaseAddr), &d.dmaMemory); err != nil { + if os.IsPermission(err) { + return true, fmt.Errorf("need more access, try as root: %v", err) + } + return true, err + } + + if err := pmem.MapAsPOD(uint64(pwmBaseAddr), &d.pwmMemory); err != nil { + return true, err + } + if err := pmem.MapAsPOD(uint64(timerBaseAddr), &d.timerMemory); err != nil { + return true, err + } + if err := pmem.MapAsPOD(uint64(clockBaseAddr), &d.clockMemory); err != nil { + return true, err + } + if err := pmem.MapAsPOD(uint64(spiBaseAddr), &d.spiMemory); err != nil { + return true, err + } + + return true, smokeTest() +} + +func (d *driverDMA) Close() error { + // Stop DMA and PWM controllers. + return nil +} + +func init() { + if false && isArm { + // TODO(maruel): This is intense, wait to be sure it works. + periph.MustRegister(&drvDMA) + } +} + +var drvDMA driverDMA diff --git a/vendor/periph.io/x/periph/host/allwinner/doc.go b/vendor/periph.io/x/periph/host/allwinner/doc.go new file mode 100644 index 0000000..c2cc83c --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/doc.go @@ -0,0 +1,33 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package allwinner exposes the GPIO functionality that is common to all +// AllWinner processors. +// +// This driver implements memory-mapped GPIO pin manipulation and leverages +// sysfs-gpio for edge detection. +// +// If you are looking at the actual implementation, open doc.go for further +// implementation details. +// +// Datasheets +// +// A64: http://files.pine64.org/doc/datasheet/pine64/Allwinner_A64_User_Manual_V1.0.pdf +// +// H3: http://dl.linux-sunxi.org/H3/Allwinner_H3_Datasheet_V1.0.pdf +// +// R8: https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20User%20Manual%20V1.1.pdf +// +// Physical overview: http://files.pine64.org/doc/datasheet/pine64/A64_Datasheet_V1.1.pdf +package allwinner + +// Other implementation details +// +// The most active kernel branch is +// https://github.com/linux-sunxi/linux-sunxi/commits/sunxi-next +// +// In particular look at +// https://github.com/linux-sunxi/linux-sunxi/blob/sunxi-next/drivers/dma/sun4i-dma.c +// and +// https://github.com/linux-sunxi/linux-sunxi/blob/sunxi-next/drivers/dma/sun6i-dma.c diff --git a/vendor/periph.io/x/periph/host/allwinner/gpio.go b/vendor/periph.io/x/periph/host/allwinner/gpio.go new file mode 100644 index 0000000..dbbf5f2 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/gpio.go @@ -0,0 +1,1065 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// This file contains the definitions of all possible generic Allwinner pins and their +// implementation using a combination of sysfs and memory-mapped I/O. + +package allwinner + +import ( + "errors" + "fmt" + "os" + "path" + "strconv" + "strings" + "time" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/pmem" + "periph.io/x/periph/host/sysfs" +) + +// List of all known pins. These global variables can be used directly. +// +// The supported functionality of each gpio differs between CPUs. For example +// the R8 has the LCD-DE signal on gpio PD25 but the A64 has it on PD19. +// +// The availability of each gpio differs between CPUs. For example the R8 has +// 19 pins in the group PB but the A64 only has 10. +// +// So make sure to read the datasheet for the exact right CPU. +var ( + PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15, PA16, PA17 *Pin + PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB20, PB21, PB22, PB23 *Pin + PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15, PC16, PC17, PC18, PC19, PC20, PC21, PC22, PC23, PC24 *Pin + PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15, PD16, PD17, PD18, PD19, PD20, PD21, PD22, PD23, PD24, PD25, PD26, PD27 *Pin + PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7, PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15, PE16, PE17 *Pin + PF0, PF1, PF2, PF3, PF4, PF5, PF6 *Pin + PG0, PG1, PG2, PG3, PG4, PG5, PG6, PG7, PG8, PG9, PG10, PG11, PG12, PG13 *Pin + PH0, PH1, PH2, PH3, PH4, PH5, PH6, PH7, PH8, PH9, PH10, PH11, PH12, PH13, PH14, PH15, PH16, PH17, PH18, PH19, PH20, PH21, PH22, PH23, PH24, PH25, PH26, PH27 *Pin + PI0, PI1, PI2, PI3, PI4, PI5, PI6, PI7, PI8, PI9, PI10, PI11, PI12, PI13, PI14, PI15, PI16, PI17, PI18, PI19, PI20, PI21 *Pin +) + +// Pin implements the gpio.PinIO interface for generic Allwinner CPU pins using +// memory mapping for gpio in/out functionality. +type Pin struct { + // Immutable. + group uint8 // as per register offset calculation + offset uint8 // as per register offset calculation + name string // name as per datasheet + defaultPull gpio.Pull // default pull at startup + + // Immutable after driver initialization. + altFunc [5]pin.Func // alternate functions + sysfsPin *sysfs.Pin // Set to the corresponding sysfs.Pin, if any. + available bool // Set when the pin is available on this CPU architecture. + supportEdge bool // Set when the pin supports interrupt based edge detection. + + // Mutable. + usingEdge bool // Set when edge detection is enabled. +} + +// String implements conn.Resource. +// +// It returns the pin name and number, ex: "PB5(37)". +func (p *Pin) String() string { + return fmt.Sprintf("%s(%d)", p.name, p.Number()) +} + +// Halt implements conn.Resource. +// +// It stops edge detection if enabled. +func (p *Pin) Halt() error { + if p.usingEdge { + if err := p.sysfsPin.Halt(); err != nil { + return p.wrap(err) + } + p.usingEdge = false + } + return nil +} + +// Name implements pin.Pin. +// +// It returns the pin name, ex: "PB5". +func (p *Pin) Name() string { + return p.name +} + +// Number implements pin.Pin. +// +// It returns the GPIO pin number as represented by gpio sysfs. +func (p *Pin) Number() int { + return int(p.group)*32 + int(p.offset) +} + +// Function implements pin.Pin. +func (p *Pin) Function() string { + return string(p.Func()) +} + +// Func implements pin.PinFunc. +func (p *Pin) Func() pin.Func { + if !p.available { + return pin.FuncNone + } + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return pin.Func("ERR") + } + return p.sysfsPin.Func() + } + switch f := p.function(); f { + case in: + if p.FastRead() { + return gpio.IN_HIGH + } + return gpio.IN_LOW + case out: + if p.FastRead() { + return gpio.OUT_HIGH + } + return gpio.OUT_LOW + case alt1: + if p.altFunc[0] != "" { + return pin.Func(p.altFunc[0]) + } + return pin.Func("ALT1") + case alt2: + if p.altFunc[1] != "" { + return pin.Func(p.altFunc[1]) + } + return pin.Func("ALT2") + case alt3: + if p.altFunc[2] != "" { + return pin.Func(p.altFunc[2]) + } + return pin.Func("ALT3") + case alt4: + if p.altFunc[3] != "" { + if strings.Contains(string(p.altFunc[3]), "EINT") { + // It's an input supporting interrupts. + if p.FastRead() { + return gpio.IN_HIGH + } + return gpio.IN_LOW + } + return pin.Func(p.altFunc[3]) + } + return pin.Func("ALT4") + case alt5: + if p.altFunc[4] != "" { + if strings.Contains(string(p.altFunc[4]), "EINT") { + // It's an input supporting interrupts. + if p.FastRead() { + return gpio.IN_HIGH + } + return gpio.IN_LOW + } + return pin.Func(p.altFunc[4]) + } + return pin.Func("ALT5") + case disabled: + return pin.FuncNone + default: + return pin.FuncNone + } +} + +// SupportedFuncs implements pin.PinFunc. +func (p *Pin) SupportedFuncs() []pin.Func { + f := make([]pin.Func, 0, 2+4) + f = append(f, gpio.IN, gpio.OUT) + for _, m := range p.altFunc { + if m != pin.FuncNone && !strings.Contains(string(m), "EINT") { + f = append(f, m) + } + } + return f +} + +// SetFunc implements pin.PinFunc. +func (p *Pin) SetFunc(f pin.Func) error { + switch f { + case gpio.FLOAT: + return p.In(gpio.Float, gpio.NoEdge) + case gpio.IN: + return p.In(gpio.PullNoChange, gpio.NoEdge) + case gpio.IN_LOW: + return p.In(gpio.PullDown, gpio.NoEdge) + case gpio.IN_HIGH: + return p.In(gpio.PullUp, gpio.NoEdge) + case gpio.OUT_HIGH: + return p.Out(gpio.High) + case gpio.OUT_LOW: + return p.Out(gpio.Low) + default: + isGeneral := f == f.Generalize() + for i, m := range p.altFunc { + if m == f || (isGeneral && m.Generalize() == f) { + if err := p.Halt(); err != nil { + return err + } + switch i { + case 0: + p.setFunction(alt1) + case 1: + p.setFunction(alt2) + case 2: + p.setFunction(alt3) + case 3: + p.setFunction(alt4) + case 4: + p.setFunction(alt5) + } + return nil + } + } + return p.wrap(errors.New("unsupported function")) + } +} + +// In implements gpio.PinIn. +// +// It sets the pin direction to input and optionally enables a pull-up/down +// resistor as well as edge detection. +// +// Not all pins support edge detection on Allwinner processors! +// +// Edge detection requires opening a gpio sysfs file handle. The pin will be +// exported at /sys/class/gpio/gpio*/. Note that the pin will not be unexported +// at shutdown. +func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error { + if !p.available { + // We do not want the error message about uninitialized system. + return p.wrap(errors.New("not available on this CPU architecture")) + } + if edge != gpio.NoEdge && !p.supportEdge { + return p.wrap(errors.New("edge detection is not supported on this pin")) + } + if p.usingEdge && edge == gpio.NoEdge { + if err := p.sysfsPin.Halt(); err != nil { + return p.wrap(err) + } + p.usingEdge = false + } + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?")) + } + if pull != gpio.PullNoChange { + return p.wrap(errors.New("pull cannot be used when subsystem gpiomem not initialized; try running as root?")) + } + if err := p.sysfsPin.In(pull, edge); err != nil { + return p.wrap(err) + } + p.usingEdge = edge != gpio.NoEdge + return nil + } + p.setFunction(in) + if pull != gpio.PullNoChange { + off := p.offset / 16 + shift := 2 * (p.offset % 16) + // Do it in a way that is concurrency safe. + drvGPIO.gpioMemory.groups[p.group].pull[off] &^= 3 << shift + switch pull { + case gpio.PullDown: + drvGPIO.gpioMemory.groups[p.group].pull[off] = 2 << shift + case gpio.PullUp: + drvGPIO.gpioMemory.groups[p.group].pull[off] = 1 << shift + default: + } + } + if edge != gpio.NoEdge { + if p.sysfsPin == nil { + return p.wrap(fmt.Errorf("pin %d is not exported by sysfs", p.Number())) + } + // This resets pending edges. + if err := p.sysfsPin.In(gpio.PullNoChange, edge); err != nil { + return p.wrap(err) + } + p.usingEdge = true + } + return nil +} + +// Read implements gpio.PinIn. +// +// It returns the current pin level. This function is fast. +func (p *Pin) Read() gpio.Level { + if !p.available { + return gpio.Low + } + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return gpio.Low + } + return p.sysfsPin.Read() + } + return gpio.Level(drvGPIO.gpioMemory.groups[p.group].data&(1<> (2 * (p.offset % 16))) & 3 { + case 0: + return gpio.Float + case 1: + return gpio.PullUp + case 2: + return gpio.PullDown + default: + // Confused. + return gpio.PullNoChange + } +} + +// DefaultPull implements gpio.PinIn. +func (p *Pin) DefaultPull() gpio.Pull { + return p.defaultPull +} + +// Out implements gpio.PinOut. +func (p *Pin) Out(l gpio.Level) error { + if !p.available { + // We do not want the error message about uninitialized system. + return p.wrap(errors.New("not available on this CPU architecture")) + } + if drvGPIO.gpioMemory == nil { + if p.sysfsPin != nil { + return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?")) + } + return p.sysfsPin.Out(l) + } + // First disable edges. + if err := p.Halt(); err != nil { + return err + } + p.FastOut(l) + p.setFunction(out) + return nil +} + +// FastOut sets a pin output level with Absolutely No error checking. +// +// Out() Must be called once first before calling FastOut(), otherwise the +// behavior is undefined. Then FastOut() can be used for minimal CPU overhead +// to reach Mhz scale bit banging. +func (p *Pin) FastOut(l gpio.Level) { + bit := uint32(1 << p.offset) + // Pn_DAT n*0x24+0x10 Port n Data Register (n from 0(A) to 8(I)) + // This is a switch on p.group rather than an index to the groups array for + // performance reasons: to avoid Go's array bound checking code. + // See https://periph.io/news/2017/gpio_perf/ for details. + switch p.group { + case 0: + if l { + drvGPIO.gpioMemory.groups[0].data |= bit + } else { + drvGPIO.gpioMemory.groups[0].data &^= bit + } + case 1: + if l { + drvGPIO.gpioMemory.groups[1].data |= bit + } else { + drvGPIO.gpioMemory.groups[1].data &^= bit + } + case 2: + if l { + drvGPIO.gpioMemory.groups[2].data |= bit + } else { + drvGPIO.gpioMemory.groups[2].data &^= bit + } + case 3: + if l { + drvGPIO.gpioMemory.groups[3].data |= bit + } else { + drvGPIO.gpioMemory.groups[3].data &^= bit + } + case 4: + if l { + drvGPIO.gpioMemory.groups[4].data |= bit + } else { + drvGPIO.gpioMemory.groups[4].data &^= bit + } + case 5: + if l { + drvGPIO.gpioMemory.groups[5].data |= bit + } else { + drvGPIO.gpioMemory.groups[5].data &^= bit + } + case 6: + if l { + drvGPIO.gpioMemory.groups[6].data |= bit + } else { + drvGPIO.gpioMemory.groups[6].data &^= bit + } + case 7: + if l { + drvGPIO.gpioMemory.groups[7].data |= bit + } else { + drvGPIO.gpioMemory.groups[7].data &^= bit + } + case 8: + if l { + drvGPIO.gpioMemory.groups[8].data |= bit + } else { + drvGPIO.gpioMemory.groups[8].data &^= bit + } + } +} + +// PWM implements gpio.PinOut. +func (p *Pin) PWM(gpio.Duty, physic.Frequency) error { + return p.wrap(errors.New("not available on this CPU architecture")) +} + +// + +// drive returns the configured output current drive strength for this GPIO. +// +// The value returned by this function is not yet verified to be correct. Use +// with suspicion. +func (p *Pin) drive() physic.ElectricCurrent { + if drvGPIO.gpioMemory == nil { + return 0 + } + // Explanation of the buffer configuration, but doesn't state what's the + // expected drive strength! + // http://files.pine64.org/doc/datasheet/pine-h64/Allwinner_H6%20V200_User_Manual_V1.1.pdf + // Section 3.21.3.4 page 381~382 + // + // The A64 and H3 datasheets call for 20mA, so it could be reasonable to + // think that the values are 5mA, 10mA, 15mA, 20mA but we don't know for + // sure. + v := (drvGPIO.gpioMemory.groups[p.group].drv[p.offset/16] >> (2 * (p.offset & 15))) & 3 + return physic.ElectricCurrent(v+1) * 5 * physic.MilliAmpere +} + +// function returns the current GPIO pin function. +func (p *Pin) function() function { + if drvGPIO.gpioMemory == nil { + return disabled + } + shift := 4 * (p.offset % 8) + return function((drvGPIO.gpioMemory.groups[p.group].cfg[p.offset/8] >> shift) & 7) +} + +// setFunction changes the GPIO pin function. +func (p *Pin) setFunction(f function) { + off := p.offset / 8 + shift := 4 * (p.offset % 8) + mask := uint32(disabled) << shift + v := (uint32(f) << shift) ^ mask + // First disable, then setup. This is concurrent safe. + drvGPIO.gpioMemory.groups[p.group].cfg[off] |= mask + drvGPIO.gpioMemory.groups[p.group].cfg[off] &^= v + if p.function() != f { + panic(f) + } +} + +func (p *Pin) wrap(err error) error { + return fmt.Errorf("allwinner-gpio (%s): %v", p, err) +} + +// + +// A64: Page 23~24 +// R8: Page 322-334. +// Each pin can have one of 7 functions. +const ( + in function = 0 + out function = 1 + alt1 function = 2 + alt2 function = 3 + alt3 function = 4 + alt4 function = 5 + alt5 function = 6 // often interrupt based edge detection as input + disabled function = 7 +) + +// cpupins that may be implemented by a generic Allwinner CPU. Not all pins +// will be present on all models and even if the CPU model supports them they +// may not be connected to anything on the board. The net effect is that it may +// look like more pins are available than really are, but trying to get the pin +// list 100% correct on all platforms seems futile, hence periph errs on the +// side of caution. +var cpupins = map[string]*Pin{ + "PA0": {group: 0, offset: 0, name: "PA0", defaultPull: gpio.Float}, + "PA1": {group: 0, offset: 1, name: "PA1", defaultPull: gpio.Float}, + "PA2": {group: 0, offset: 2, name: "PA2", defaultPull: gpio.Float}, + "PA3": {group: 0, offset: 3, name: "PA3", defaultPull: gpio.Float}, + "PA4": {group: 0, offset: 4, name: "PA4", defaultPull: gpio.Float}, + "PA5": {group: 0, offset: 5, name: "PA5", defaultPull: gpio.Float}, + "PA6": {group: 0, offset: 6, name: "PA6", defaultPull: gpio.Float}, + "PA7": {group: 0, offset: 7, name: "PA7", defaultPull: gpio.Float}, + "PA8": {group: 0, offset: 8, name: "PA8", defaultPull: gpio.Float}, + "PA9": {group: 0, offset: 9, name: "PA9", defaultPull: gpio.Float}, + "PA10": {group: 0, offset: 10, name: "PA10", defaultPull: gpio.Float}, + "PA11": {group: 0, offset: 11, name: "PA11", defaultPull: gpio.Float}, + "PA12": {group: 0, offset: 12, name: "PA12", defaultPull: gpio.Float}, + "PA13": {group: 0, offset: 13, name: "PA13", defaultPull: gpio.Float}, + "PA14": {group: 0, offset: 14, name: "PA14", defaultPull: gpio.Float}, + "PA15": {group: 0, offset: 15, name: "PA15", defaultPull: gpio.Float}, + "PA16": {group: 0, offset: 16, name: "PA16", defaultPull: gpio.Float}, + "PA17": {group: 0, offset: 17, name: "PA17", defaultPull: gpio.Float}, + "PB0": {group: 1, offset: 0, name: "PB0", defaultPull: gpio.Float}, + "PB1": {group: 1, offset: 1, name: "PB1", defaultPull: gpio.Float}, + "PB2": {group: 1, offset: 2, name: "PB2", defaultPull: gpio.Float}, + "PB3": {group: 1, offset: 3, name: "PB3", defaultPull: gpio.Float}, + "PB4": {group: 1, offset: 4, name: "PB4", defaultPull: gpio.Float}, + "PB5": {group: 1, offset: 5, name: "PB5", defaultPull: gpio.Float}, + "PB6": {group: 1, offset: 6, name: "PB6", defaultPull: gpio.Float}, + "PB7": {group: 1, offset: 7, name: "PB7", defaultPull: gpio.Float}, + "PB8": {group: 1, offset: 8, name: "PB8", defaultPull: gpio.Float}, + "PB9": {group: 1, offset: 9, name: "PB9", defaultPull: gpio.Float}, + "PB10": {group: 1, offset: 10, name: "PB10", defaultPull: gpio.Float}, + "PB11": {group: 1, offset: 11, name: "PB11", defaultPull: gpio.Float}, + "PB12": {group: 1, offset: 12, name: "PB12", defaultPull: gpio.Float}, + "PB13": {group: 1, offset: 13, name: "PB13", defaultPull: gpio.Float}, + "PB14": {group: 1, offset: 14, name: "PB14", defaultPull: gpio.Float}, + "PB15": {group: 1, offset: 15, name: "PB15", defaultPull: gpio.Float}, + "PB16": {group: 1, offset: 16, name: "PB16", defaultPull: gpio.Float}, + "PB17": {group: 1, offset: 17, name: "PB17", defaultPull: gpio.Float}, + "PB18": {group: 1, offset: 18, name: "PB18", defaultPull: gpio.Float}, + "PB19": {group: 1, offset: 19, name: "PB19", defaultPull: gpio.Float}, + "PB20": {group: 1, offset: 20, name: "PB20", defaultPull: gpio.Float}, + "PB21": {group: 1, offset: 21, name: "PB21", defaultPull: gpio.Float}, + "PB22": {group: 1, offset: 22, name: "PB22", defaultPull: gpio.Float}, + "PB23": {group: 1, offset: 23, name: "PB23", defaultPull: gpio.Float}, + "PC0": {group: 2, offset: 0, name: "PC0", defaultPull: gpio.Float}, + "PC1": {group: 2, offset: 1, name: "PC1", defaultPull: gpio.Float}, + "PC2": {group: 2, offset: 2, name: "PC2", defaultPull: gpio.Float}, + "PC3": {group: 2, offset: 3, name: "PC3", defaultPull: gpio.PullUp}, + "PC4": {group: 2, offset: 4, name: "PC4", defaultPull: gpio.PullUp}, + "PC5": {group: 2, offset: 5, name: "PC5", defaultPull: gpio.Float}, + "PC6": {group: 2, offset: 6, name: "PC6", defaultPull: gpio.PullUp}, + "PC7": {group: 2, offset: 7, name: "PC7", defaultPull: gpio.PullUp}, + "PC8": {group: 2, offset: 8, name: "PC8", defaultPull: gpio.Float}, + "PC9": {group: 2, offset: 9, name: "PC9", defaultPull: gpio.Float}, + "PC10": {group: 2, offset: 10, name: "PC10", defaultPull: gpio.Float}, + "PC11": {group: 2, offset: 11, name: "PC11", defaultPull: gpio.Float}, + "PC12": {group: 2, offset: 12, name: "PC12", defaultPull: gpio.Float}, + "PC13": {group: 2, offset: 13, name: "PC13", defaultPull: gpio.Float}, + "PC14": {group: 2, offset: 14, name: "PC14", defaultPull: gpio.Float}, + "PC15": {group: 2, offset: 15, name: "PC15", defaultPull: gpio.Float}, + "PC16": {group: 2, offset: 16, name: "PC16", defaultPull: gpio.Float}, + "PC17": {group: 2, offset: 17, name: "PC17", defaultPull: gpio.Float}, + "PC18": {group: 2, offset: 18, name: "PC18", defaultPull: gpio.Float}, + "PC19": {group: 2, offset: 19, name: "PC19", defaultPull: gpio.Float}, + "PC20": {group: 2, offset: 20, name: "PC20", defaultPull: gpio.Float}, + "PC21": {group: 2, offset: 21, name: "PC21", defaultPull: gpio.Float}, + "PC22": {group: 2, offset: 22, name: "PC22", defaultPull: gpio.Float}, + "PC23": {group: 2, offset: 23, name: "PC23", defaultPull: gpio.Float}, + "PC24": {group: 2, offset: 24, name: "PC24", defaultPull: gpio.Float}, + "PD0": {group: 3, offset: 0, name: "PD0", defaultPull: gpio.Float}, + "PD1": {group: 3, offset: 1, name: "PD1", defaultPull: gpio.Float}, + "PD2": {group: 3, offset: 2, name: "PD2", defaultPull: gpio.Float}, + "PD3": {group: 3, offset: 3, name: "PD3", defaultPull: gpio.Float}, + "PD4": {group: 3, offset: 4, name: "PD4", defaultPull: gpio.Float}, + "PD5": {group: 3, offset: 5, name: "PD5", defaultPull: gpio.Float}, + "PD6": {group: 3, offset: 6, name: "PD6", defaultPull: gpio.Float}, + "PD7": {group: 3, offset: 7, name: "PD7", defaultPull: gpio.Float}, + "PD8": {group: 3, offset: 8, name: "PD8", defaultPull: gpio.Float}, + "PD9": {group: 3, offset: 9, name: "PD9", defaultPull: gpio.Float}, + "PD10": {group: 3, offset: 10, name: "PD10", defaultPull: gpio.Float}, + "PD11": {group: 3, offset: 11, name: "PD11", defaultPull: gpio.Float}, + "PD12": {group: 3, offset: 12, name: "PD12", defaultPull: gpio.Float}, + "PD13": {group: 3, offset: 13, name: "PD13", defaultPull: gpio.Float}, + "PD14": {group: 3, offset: 14, name: "PD14", defaultPull: gpio.Float}, + "PD15": {group: 3, offset: 15, name: "PD15", defaultPull: gpio.Float}, + "PD16": {group: 3, offset: 16, name: "PD16", defaultPull: gpio.Float}, + "PD17": {group: 3, offset: 17, name: "PD17", defaultPull: gpio.Float}, + "PD18": {group: 3, offset: 18, name: "PD18", defaultPull: gpio.Float}, + "PD19": {group: 3, offset: 19, name: "PD19", defaultPull: gpio.Float}, + "PD20": {group: 3, offset: 20, name: "PD20", defaultPull: gpio.Float}, + "PD21": {group: 3, offset: 21, name: "PD21", defaultPull: gpio.Float}, + "PD22": {group: 3, offset: 22, name: "PD22", defaultPull: gpio.Float}, + "PD23": {group: 3, offset: 23, name: "PD23", defaultPull: gpio.Float}, + "PD24": {group: 3, offset: 24, name: "PD24", defaultPull: gpio.Float}, + "PD25": {group: 3, offset: 25, name: "PD25", defaultPull: gpio.Float}, + "PD26": {group: 3, offset: 26, name: "PD26", defaultPull: gpio.Float}, + "PD27": {group: 3, offset: 27, name: "PD27", defaultPull: gpio.Float}, + "PE0": {group: 4, offset: 0, name: "PE0", defaultPull: gpio.Float}, + "PE1": {group: 4, offset: 1, name: "PE1", defaultPull: gpio.Float}, + "PE2": {group: 4, offset: 2, name: "PE2", defaultPull: gpio.Float}, + "PE3": {group: 4, offset: 3, name: "PE3", defaultPull: gpio.Float}, + "PE4": {group: 4, offset: 4, name: "PE4", defaultPull: gpio.Float}, + "PE5": {group: 4, offset: 5, name: "PE5", defaultPull: gpio.Float}, + "PE6": {group: 4, offset: 6, name: "PE6", defaultPull: gpio.Float}, + "PE7": {group: 4, offset: 7, name: "PE7", defaultPull: gpio.Float}, + "PE8": {group: 4, offset: 8, name: "PE8", defaultPull: gpio.Float}, + "PE9": {group: 4, offset: 9, name: "PE9", defaultPull: gpio.Float}, + "PE10": {group: 4, offset: 10, name: "PE10", defaultPull: gpio.Float}, + "PE11": {group: 4, offset: 11, name: "PE11", defaultPull: gpio.Float}, + "PE12": {group: 4, offset: 12, name: "PE12", defaultPull: gpio.Float}, + "PE13": {group: 4, offset: 13, name: "PE13", defaultPull: gpio.Float}, + "PE14": {group: 4, offset: 14, name: "PE14", defaultPull: gpio.Float}, + "PE15": {group: 4, offset: 15, name: "PE15", defaultPull: gpio.Float}, + "PE16": {group: 4, offset: 16, name: "PE16", defaultPull: gpio.Float}, + "PE17": {group: 4, offset: 17, name: "PE17", defaultPull: gpio.Float}, + "PF0": {group: 5, offset: 0, name: "PF0", defaultPull: gpio.Float}, + "PF1": {group: 5, offset: 1, name: "PF1", defaultPull: gpio.Float}, + "PF2": {group: 5, offset: 2, name: "PF2", defaultPull: gpio.Float}, + "PF3": {group: 5, offset: 3, name: "PF3", defaultPull: gpio.Float}, + "PF4": {group: 5, offset: 4, name: "PF4", defaultPull: gpio.Float}, + "PF5": {group: 5, offset: 5, name: "PF5", defaultPull: gpio.Float}, + "PF6": {group: 5, offset: 6, name: "PF6", defaultPull: gpio.Float}, + "PG0": {group: 6, offset: 0, name: "PG0", defaultPull: gpio.Float}, + "PG1": {group: 6, offset: 1, name: "PG1", defaultPull: gpio.Float}, + "PG2": {group: 6, offset: 2, name: "PG2", defaultPull: gpio.Float}, + "PG3": {group: 6, offset: 3, name: "PG3", defaultPull: gpio.Float}, + "PG4": {group: 6, offset: 4, name: "PG4", defaultPull: gpio.Float}, + "PG5": {group: 6, offset: 5, name: "PG5", defaultPull: gpio.Float}, + "PG6": {group: 6, offset: 6, name: "PG6", defaultPull: gpio.Float}, + "PG7": {group: 6, offset: 7, name: "PG7", defaultPull: gpio.Float}, + "PG8": {group: 6, offset: 8, name: "PG8", defaultPull: gpio.Float}, + "PG9": {group: 6, offset: 9, name: "PG9", defaultPull: gpio.Float}, + "PG10": {group: 6, offset: 10, name: "PG10", defaultPull: gpio.Float}, + "PG11": {group: 6, offset: 11, name: "PG11", defaultPull: gpio.Float}, + "PG12": {group: 6, offset: 12, name: "PG12", defaultPull: gpio.Float}, + "PG13": {group: 6, offset: 13, name: "PG13", defaultPull: gpio.Float}, + "PH0": {group: 7, offset: 0, name: "PH0", defaultPull: gpio.Float}, + "PH1": {group: 7, offset: 1, name: "PH1", defaultPull: gpio.Float}, + "PH2": {group: 7, offset: 2, name: "PH2", defaultPull: gpio.Float}, + "PH3": {group: 7, offset: 3, name: "PH3", defaultPull: gpio.Float}, + "PH4": {group: 7, offset: 4, name: "PH4", defaultPull: gpio.Float}, + "PH5": {group: 7, offset: 5, name: "PH5", defaultPull: gpio.Float}, + "PH6": {group: 7, offset: 6, name: "PH6", defaultPull: gpio.Float}, + "PH7": {group: 7, offset: 7, name: "PH7", defaultPull: gpio.Float}, + "PH8": {group: 7, offset: 8, name: "PH8", defaultPull: gpio.Float}, + "PH9": {group: 7, offset: 9, name: "PH9", defaultPull: gpio.Float}, + "PH10": {group: 7, offset: 10, name: "PH10", defaultPull: gpio.Float}, + "PH11": {group: 7, offset: 11, name: "PH11", defaultPull: gpio.Float}, + "PH12": {group: 7, offset: 12, name: "PH12", defaultPull: gpio.Float}, + "PH13": {group: 7, offset: 13, name: "PH13", defaultPull: gpio.Float}, + "PH14": {group: 7, offset: 14, name: "PH14", defaultPull: gpio.Float}, + "PH15": {group: 7, offset: 15, name: "PH15", defaultPull: gpio.Float}, + "PH16": {group: 7, offset: 16, name: "PH16", defaultPull: gpio.Float}, + "PH17": {group: 7, offset: 17, name: "PH17", defaultPull: gpio.Float}, + "PH18": {group: 7, offset: 18, name: "PH18", defaultPull: gpio.Float}, + "PH19": {group: 7, offset: 19, name: "PH19", defaultPull: gpio.Float}, + "PH20": {group: 7, offset: 20, name: "PH20", defaultPull: gpio.Float}, + "PH21": {group: 7, offset: 21, name: "PH21", defaultPull: gpio.Float}, + "PH22": {group: 7, offset: 22, name: "PH22", defaultPull: gpio.Float}, + "PH23": {group: 7, offset: 23, name: "PH23", defaultPull: gpio.Float}, + "PH24": {group: 7, offset: 24, name: "PH24", defaultPull: gpio.Float}, + "PH25": {group: 7, offset: 25, name: "PH25", defaultPull: gpio.Float}, + "PH26": {group: 7, offset: 26, name: "PH26", defaultPull: gpio.Float}, + "PH27": {group: 7, offset: 27, name: "PH27", defaultPull: gpio.Float}, + "PI0": {group: 8, offset: 0, name: "PI0", defaultPull: gpio.Float}, + "PI1": {group: 8, offset: 1, name: "PI1", defaultPull: gpio.Float}, + "PI2": {group: 8, offset: 2, name: "PI2", defaultPull: gpio.Float}, + "PI3": {group: 8, offset: 3, name: "PI3", defaultPull: gpio.Float}, + "PI4": {group: 8, offset: 4, name: "PI4", defaultPull: gpio.Float}, + "PI5": {group: 8, offset: 5, name: "PI5", defaultPull: gpio.Float}, + "PI6": {group: 8, offset: 6, name: "PI6", defaultPull: gpio.Float}, + "PI7": {group: 8, offset: 7, name: "PI7", defaultPull: gpio.Float}, + "PI8": {group: 8, offset: 8, name: "PI8", defaultPull: gpio.Float}, + "PI9": {group: 8, offset: 9, name: "PI9", defaultPull: gpio.Float}, + "PI10": {group: 8, offset: 10, name: "PI10", defaultPull: gpio.Float}, + "PI11": {group: 8, offset: 11, name: "PI11", defaultPull: gpio.Float}, + "PI12": {group: 8, offset: 12, name: "PI12", defaultPull: gpio.Float}, + "PI13": {group: 8, offset: 13, name: "PI13", defaultPull: gpio.Float}, + "PI14": {group: 8, offset: 14, name: "PI14", defaultPull: gpio.Float}, + "PI15": {group: 8, offset: 15, name: "PI15", defaultPull: gpio.Float}, + "PI16": {group: 8, offset: 16, name: "PI16", defaultPull: gpio.Float}, + "PI17": {group: 8, offset: 17, name: "PI17", defaultPull: gpio.Float}, + "PI18": {group: 8, offset: 18, name: "PI18", defaultPull: gpio.Float}, + "PI19": {group: 8, offset: 19, name: "PI19", defaultPull: gpio.Float}, + "PI20": {group: 8, offset: 20, name: "PI20", defaultPull: gpio.Float}, + "PI21": {group: 8, offset: 21, name: "PI21", defaultPull: gpio.Float}, +} + +func init() { + PA0 = cpupins["PA0"] + PA1 = cpupins["PA1"] + PA2 = cpupins["PA2"] + PA3 = cpupins["PA3"] + PA4 = cpupins["PA4"] + PA5 = cpupins["PA5"] + PA6 = cpupins["PA6"] + PA7 = cpupins["PA7"] + PA8 = cpupins["PA8"] + PA9 = cpupins["PA9"] + PA10 = cpupins["PA10"] + PA11 = cpupins["PA11"] + PA12 = cpupins["PA12"] + PA13 = cpupins["PA13"] + PA14 = cpupins["PA14"] + PA15 = cpupins["PA15"] + PA16 = cpupins["PA16"] + PA17 = cpupins["PA17"] + PB0 = cpupins["PB0"] + PB1 = cpupins["PB1"] + PB2 = cpupins["PB2"] + PB3 = cpupins["PB3"] + PB4 = cpupins["PB4"] + PB5 = cpupins["PB5"] + PB6 = cpupins["PB6"] + PB7 = cpupins["PB7"] + PB8 = cpupins["PB8"] + PB9 = cpupins["PB9"] + PB10 = cpupins["PB10"] + PB11 = cpupins["PB11"] + PB12 = cpupins["PB12"] + PB13 = cpupins["PB13"] + PB14 = cpupins["PB14"] + PB15 = cpupins["PB15"] + PB16 = cpupins["PB16"] + PB17 = cpupins["PB17"] + PB18 = cpupins["PB18"] + PB19 = cpupins["PB19"] + PB20 = cpupins["PB20"] + PB21 = cpupins["PB21"] + PB22 = cpupins["PB22"] + PB23 = cpupins["PB23"] + PC0 = cpupins["PC0"] + PC1 = cpupins["PC1"] + PC2 = cpupins["PC2"] + PC3 = cpupins["PC3"] + PC4 = cpupins["PC4"] + PC5 = cpupins["PC5"] + PC6 = cpupins["PC6"] + PC7 = cpupins["PC7"] + PC8 = cpupins["PC8"] + PC9 = cpupins["PC9"] + PC10 = cpupins["PC10"] + PC11 = cpupins["PC11"] + PC12 = cpupins["PC12"] + PC13 = cpupins["PC13"] + PC14 = cpupins["PC14"] + PC15 = cpupins["PC15"] + PC16 = cpupins["PC16"] + PC17 = cpupins["PC17"] + PC18 = cpupins["PC18"] + PC19 = cpupins["PC19"] + PC20 = cpupins["PC20"] + PC21 = cpupins["PC21"] + PC22 = cpupins["PC22"] + PC23 = cpupins["PC23"] + PC24 = cpupins["PC24"] + PD0 = cpupins["PD0"] + PD1 = cpupins["PD1"] + PD2 = cpupins["PD2"] + PD3 = cpupins["PD3"] + PD4 = cpupins["PD4"] + PD5 = cpupins["PD5"] + PD6 = cpupins["PD6"] + PD7 = cpupins["PD7"] + PD8 = cpupins["PD8"] + PD9 = cpupins["PD9"] + PD10 = cpupins["PD10"] + PD11 = cpupins["PD11"] + PD12 = cpupins["PD12"] + PD13 = cpupins["PD13"] + PD14 = cpupins["PD14"] + PD15 = cpupins["PD15"] + PD16 = cpupins["PD16"] + PD17 = cpupins["PD17"] + PD18 = cpupins["PD18"] + PD19 = cpupins["PD19"] + PD20 = cpupins["PD20"] + PD21 = cpupins["PD21"] + PD22 = cpupins["PD22"] + PD23 = cpupins["PD23"] + PD24 = cpupins["PD24"] + PD25 = cpupins["PD25"] + PD26 = cpupins["PD26"] + PD27 = cpupins["PD27"] + PE0 = cpupins["PE0"] + PE1 = cpupins["PE1"] + PE2 = cpupins["PE2"] + PE3 = cpupins["PE3"] + PE4 = cpupins["PE4"] + PE5 = cpupins["PE5"] + PE6 = cpupins["PE6"] + PE7 = cpupins["PE7"] + PE8 = cpupins["PE8"] + PE9 = cpupins["PE9"] + PE10 = cpupins["PE10"] + PE11 = cpupins["PE11"] + PE12 = cpupins["PE12"] + PE13 = cpupins["PE13"] + PE14 = cpupins["PE14"] + PE15 = cpupins["PE15"] + PE16 = cpupins["PE16"] + PE17 = cpupins["PE17"] + PF0 = cpupins["PF0"] + PF1 = cpupins["PF1"] + PF2 = cpupins["PF2"] + PF3 = cpupins["PF3"] + PF4 = cpupins["PF4"] + PF5 = cpupins["PF5"] + PF6 = cpupins["PF6"] + PG0 = cpupins["PG0"] + PG1 = cpupins["PG1"] + PG2 = cpupins["PG2"] + PG3 = cpupins["PG3"] + PG4 = cpupins["PG4"] + PG5 = cpupins["PG5"] + PG6 = cpupins["PG6"] + PG7 = cpupins["PG7"] + PG8 = cpupins["PG8"] + PG9 = cpupins["PG9"] + PG10 = cpupins["PG10"] + PG11 = cpupins["PG11"] + PG12 = cpupins["PG12"] + PG13 = cpupins["PG13"] + PH0 = cpupins["PH0"] + PH1 = cpupins["PH1"] + PH2 = cpupins["PH2"] + PH3 = cpupins["PH3"] + PH4 = cpupins["PH4"] + PH5 = cpupins["PH5"] + PH6 = cpupins["PH6"] + PH7 = cpupins["PH7"] + PH8 = cpupins["PH8"] + PH9 = cpupins["PH9"] + PH10 = cpupins["PH10"] + PH11 = cpupins["PH11"] + PH12 = cpupins["PH12"] + PH13 = cpupins["PH13"] + PH14 = cpupins["PH14"] + PH15 = cpupins["PH15"] + PH16 = cpupins["PH16"] + PH17 = cpupins["PH17"] + PH18 = cpupins["PH18"] + PH19 = cpupins["PH19"] + PH20 = cpupins["PH20"] + PH21 = cpupins["PH21"] + PH22 = cpupins["PH22"] + PH23 = cpupins["PH23"] + PH24 = cpupins["PH24"] + PH25 = cpupins["PH25"] + PH26 = cpupins["PH26"] + PH27 = cpupins["PH27"] + PI0 = cpupins["PI0"] + PI1 = cpupins["PI1"] + PI2 = cpupins["PI2"] + PI3 = cpupins["PI3"] + PI4 = cpupins["PI4"] + PI5 = cpupins["PI5"] + PI6 = cpupins["PI6"] + PI7 = cpupins["PI7"] + PI8 = cpupins["PI8"] + PI9 = cpupins["PI9"] + PI10 = cpupins["PI10"] + PI11 = cpupins["PI11"] + PI12 = cpupins["PI12"] + PI13 = cpupins["PI13"] + PI14 = cpupins["PI14"] + PI15 = cpupins["PI15"] + PI16 = cpupins["PI16"] + PI17 = cpupins["PI17"] + PI18 = cpupins["PI18"] + PI19 = cpupins["PI19"] + PI20 = cpupins["PI20"] + PI21 = cpupins["PI21"] +} + +// initPins initializes the mapping of pins by function, sets the alternate +// functions of each pin, and registers all the pins with gpio. +func initPins() error { + functions := map[pin.Func]struct{}{} + for name, p := range cpupins { + num := strconv.Itoa(p.Number()) + gpion := "GPIO" + num + + // Unregister the pin if already registered. This happens with sysfs-gpio. + // Do not error on it, since sysfs-gpio may have failed to load. + _ = gpioreg.Unregister(gpion) + _ = gpioreg.Unregister(num) + + // Register the pin with gpio. + if err := gpioreg.Register(p); err != nil { + return err + } + if err := gpioreg.RegisterAlias(gpion, name); err != nil { + return err + } + if err := gpioreg.RegisterAlias(num, name); err != nil { + return err + } + switch f := p.Func(); f { + case gpio.IN, gpio.OUT, pin.FuncNone: + default: + // Registering the same alias twice fails. This can happen if two pins + // are configured with the same function. + if _, ok := functions[f]; !ok { + functions[f] = struct{}{} + if err := gpioreg.RegisterAlias(string(f), name); err != nil { + return err + } + } + } + } + + // Now do a second loop but do the alternate functions. + for name, p := range cpupins { + for _, f := range p.SupportedFuncs() { + switch f { + case gpio.IN, gpio.OUT: + default: + if _, ok := functions[f]; !ok { + functions[f] = struct{}{} + if err := gpioreg.RegisterAlias(string(f), name); err != nil { + return err + } + } + } + } + } + return nil +} + +// function encodes the active functionality of a pin. The alternate functions +// are GPIO pin dependent. +type function uint8 + +// gpioGroup is a memory-mapped structure for the hardware registers that +// control a group of at most 32 pins. In practice the number of valid pins per +// group varies between 10 and 27. +// +// http://files.pine64.org/doc/datasheet/pine64/Allwinner_A64_User_Manual_V1.0.pdf +// Page 376 GPIO PB to PH. +// Page 410 GPIO PL. +// Size is 36 bytes. +type gpioGroup struct { + // Pn_CFGx n*0x24+x*4 Port n Configure Register x (n from 1(B) to 7(H)) + cfg [4]uint32 + // Pn_DAT n*0x24+0x10 Port n Data Register (n from 1(B) to 7(H)) + data uint32 + // Pn_DRVx n*0x24+0x14+x*4 Port n Multi-Driving Register x (n from 1 to 7) + drv [2]uint32 + // Pn_PULL n*0x24+0x1C+x*4 Port n Pull Register (n from 1(B) to 7(H)) + pull [2]uint32 +} + +// gpioMap memory-maps all the gpio pin groups. +type gpioMap struct { + // PA to PI. + groups [9]gpioGroup +} + +// driverGPIO implements periph.Driver. +type driverGPIO struct { + // gpioMemory is the memory map of the CPU GPIO registers. + gpioMemory *gpioMap +} + +func (d *driverGPIO) String() string { + return "allwinner-gpio" +} + +func (d *driverGPIO) Prerequisites() []string { + return nil +} + +func (d *driverGPIO) After() []string { + return []string{"sysfs-gpio"} +} + +// Init does nothing if an allwinner processor is not detected. If one is +// detected, it memory maps gpio CPU registers and then sets up the pin mapping +// for the exact processor model detected. +func (d *driverGPIO) Init() (bool, error) { + if !Present() { + return false, errors.New("Allwinner CPU not detected") + } + + // Mark the right pins as available even if the memory map fails so they can + // callback to sysfs.Pins. + switch { + case IsA64(): + if err := mapA64Pins(); err != nil { + return true, err + } + case IsR8(): + if err := mapR8Pins(); err != nil { + return true, err + } + case IsA20(): + if err := mapA20Pins(); err != nil { + return true, err + } + default: + return false, errors.New("unknown Allwinner CPU model") + } + + // gpioBaseAddr is the physical base address of the GPIO registers. + gpioBaseAddr := uint32(getBaseAddress()) + if err := pmem.MapAsPOD(uint64(gpioBaseAddr), &d.gpioMemory); err != nil { + if os.IsPermission(err) { + return true, fmt.Errorf("need more access, try as root: %v", err) + } + return true, err + } + + return true, initPins() +} + +func init() { + if isArm { + periph.MustRegister(&drvGPIO) + } +} + +// getBaseAddress queries the virtual file system to retrieve the base address +// of the GPIO registers for GPIO pins in groups PA to PI. +// +// Defaults to 0x01C20800 as per datasheet if it could not query the file +// system. +func getBaseAddress() uint64 { + base := uint64(0x01C20800) + link, err := os.Readlink("/sys/bus/platform/drivers/sun50i-pinctrl/driver") + if err != nil { + return base + } + parts := strings.SplitN(path.Base(link), ".", 2) + if len(parts) != 2 { + return base + } + base2, err := strconv.ParseUint(parts[0], 16, 64) + if err != nil { + return base + } + return base2 +} + +var drvGPIO driverGPIO + +// Ensure that the various structs implement the interfaces they're supposed to. +var _ gpio.PinIO = &Pin{} +var _ gpio.PinIn = &Pin{} +var _ gpio.PinOut = &Pin{} +var _ pin.PinFunc = &Pin{} diff --git a/vendor/periph.io/x/periph/host/allwinner/gpio_pl.go b/vendor/periph.io/x/periph/host/allwinner/gpio_pl.go new file mode 100644 index 0000000..c29475c --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/gpio_pl.go @@ -0,0 +1,563 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +import ( + "errors" + "fmt" + "os" + "path" + "strconv" + "strings" + "time" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/pmem" + "periph.io/x/periph/host/sysfs" +) + +// All the pins in the PL group. +var PL0, PL1, PL2, PL3, PL4, PL5, PL6, PL7, PL8, PL9, PL10, PL11, PL12 *PinPL + +// PinPL defines one CPU supported pin in the PL group. +// +// PinPL implements gpio.PinIO. +type PinPL struct { + // Immutable. + offset uint8 // as per register offset calculation + name string // name as per datasheet + defaultPull gpio.Pull // default pull at startup + + // Immutable after driver initialization. + sysfsPin *sysfs.Pin // Set to the corresponding sysfs.Pin, if any. + available bool // Set when the pin is available on this CPU architecture. + + // Mutable. + usingEdge bool // Set when edge detection is enabled. +} + +// String implements conn.Resource. +// +// It returns the pin name and number, ex: "PL5(352)". +func (p *PinPL) String() string { + return fmt.Sprintf("%s(%d)", p.name, p.Number()) +} + +// Halt implements conn.Resource. +// +// It stops edge detection if enabled. +func (p *PinPL) Halt() error { + if p.usingEdge { + if err := p.sysfsPin.Halt(); err != nil { + return p.wrap(err) + } + p.usingEdge = false + } + return nil +} + +// Name implements pin.Pin. +// +// It returns the pin name, ex: "PL5". +func (p *PinPL) Name() string { + return p.name +} + +// Number implements pin.Pin. +// +// It returns the GPIO pin number as represented by gpio sysfs. +func (p *PinPL) Number() int { + return 11*32 + int(p.offset) +} + +// Function implements pin.Pin. +func (p *PinPL) Function() string { + return string(p.Func()) +} + +// Func implements pin.PinFunc. +func (p *PinPL) Func() pin.Func { + if !p.available { + // We do not want the error message about uninitialized system. + return pin.FuncNone + } + if drvGPIOPL.gpioMemoryPL == nil { + if p.sysfsPin == nil { + return pin.Func("ERR") + } + return p.sysfsPin.Func() + } + switch f := p.function(); f { + case in: + if p.FastRead() { + return gpio.IN_HIGH + } + return gpio.IN_LOW + case out: + if p.FastRead() { + return gpio.OUT_HIGH + } + return gpio.OUT_LOW + case alt1: + if s := mappingPL[p.offset][0]; len(s) != 0 { + return pin.Func(s) + } + return pin.Func("ALT1") + case alt2: + if s := mappingPL[p.offset][1]; len(s) != 0 { + return pin.Func(s) + } + return pin.Func("ALT2") + case alt3: + if s := mappingPL[p.offset][2]; len(s) != 0 { + return pin.Func(s) + } + return pin.Func("ALT3") + case alt4: + if s := mappingPL[p.offset][3]; len(s) != 0 { + return pin.Func(s) + } + return pin.Func("ALT4") + case alt5: + if s := mappingPL[p.offset][4]; len(s) != 0 { + if strings.Contains(string(s), "_EINT") { + // It's an input supporting interrupts. + if p.FastRead() { + return gpio.IN_HIGH + } + return gpio.IN_LOW + } + return pin.Func(s) + } + return pin.Func("ALT5") + case disabled: + return pin.FuncNone + default: + return pin.FuncNone + } +} + +// SupportedFuncs implements pin.PinFunc. +func (p *PinPL) SupportedFuncs() []pin.Func { + f := make([]pin.Func, 0, 2+2) + f = append(f, gpio.IN, gpio.OUT) + for _, m := range mappingPL[p.offset] { + if m != pin.FuncNone && !strings.Contains(string(m), "_EINT") { + f = append(f, m) + } + } + return f +} + +// SetFunc implements pin.PinFunc. +func (p *PinPL) SetFunc(f pin.Func) error { + switch f { + case gpio.FLOAT: + return p.In(gpio.Float, gpio.NoEdge) + case gpio.IN: + return p.In(gpio.PullNoChange, gpio.NoEdge) + case gpio.IN_LOW: + return p.In(gpio.PullDown, gpio.NoEdge) + case gpio.IN_HIGH: + return p.In(gpio.PullUp, gpio.NoEdge) + case gpio.OUT_HIGH: + return p.Out(gpio.High) + case gpio.OUT_LOW: + return p.Out(gpio.Low) + default: + isGeneral := f == f.Generalize() + for i, m := range mappingPL[p.offset] { + if m == f || (isGeneral && m.Generalize() == f) { + if err := p.Halt(); err != nil { + return err + } + switch i { + case 0: + p.setFunction(alt1) + case 1: + p.setFunction(alt2) + case 2: + p.setFunction(alt3) + case 3: + p.setFunction(alt4) + case 4: + p.setFunction(alt5) + } + return nil + } + } + return p.wrap(errors.New("unsupported function")) + } +} + +// In implements gpio.PinIn. +func (p *PinPL) In(pull gpio.Pull, edge gpio.Edge) error { + if !p.available { + // We do not want the error message about uninitialized system. + return p.wrap(errors.New("not available on this CPU architecture")) + } + if p.usingEdge && edge == gpio.NoEdge { + if err := p.sysfsPin.Halt(); err != nil { + return p.wrap(err) + } + p.usingEdge = false + } + if drvGPIOPL.gpioMemoryPL == nil { + if p.sysfsPin == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?")) + } + if pull != gpio.PullNoChange { + return p.wrap(errors.New("pull cannot be used when subsystem gpiomem not initialized; try running as root?")) + } + if err := p.sysfsPin.In(pull, edge); err != nil { + return p.wrap(err) + } + p.usingEdge = edge != gpio.NoEdge + return nil + } + if !p.setFunction(in) { + return p.wrap(errors.New("failed to set pin as input")) + } + if pull != gpio.PullNoChange { + off := p.offset / 16 + shift := 2 * (p.offset % 16) + // Do it in a way that is concurrent safe. + drvGPIOPL.gpioMemoryPL.pull[off] &^= 3 << shift + switch pull { + case gpio.PullDown: + drvGPIOPL.gpioMemoryPL.pull[off] = 2 << shift + case gpio.PullUp: + drvGPIOPL.gpioMemoryPL.pull[off] = 1 << shift + default: + } + } + if edge != gpio.NoEdge { + if p.sysfsPin == nil { + return p.wrap(fmt.Errorf("pin %d is not exported by sysfs", p.Number())) + } + // This resets pending edges. + if err := p.sysfsPin.In(gpio.PullNoChange, edge); err != nil { + return p.wrap(err) + } + p.usingEdge = true + } + return nil +} + +// Read implements gpio.PinIn. +func (p *PinPL) Read() gpio.Level { + if drvGPIOPL.gpioMemoryPL == nil { + if p.sysfsPin == nil { + return gpio.Low + } + return p.sysfsPin.Read() + } + return gpio.Level(drvGPIOPL.gpioMemoryPL.data&(1<> (2 * (p.offset % 16))) & 3 { + case 0: + return gpio.Float + case 1: + return gpio.PullUp + case 2: + return gpio.PullDown + default: + // Confused. + return gpio.PullNoChange + } +} + +// DefaultPull implements gpio.PinIn. +func (p *PinPL) DefaultPull() gpio.Pull { + return p.defaultPull +} + +// Out implements gpio.PinOut. +func (p *PinPL) Out(l gpio.Level) error { + if !p.available { + // We do not want the error message about uninitialized system. + return p.wrap(errors.New("not available on this CPU architecture")) + } + if drvGPIOPL.gpioMemoryPL == nil { + if p.sysfsPin != nil { + return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible; try running as root?")) + } + return p.sysfsPin.Out(l) + } + // First disable edges. + if err := p.Halt(); err != nil { + return err + } + p.FastOut(l) + if !p.setFunction(out) { + return p.wrap(errors.New("failed to set pin as output")) + } + return nil +} + +// FastOut sets a pin output level with Absolutely No error checking. +// +// See Pin.FastOut for more information. +func (p *PinPL) FastOut(l gpio.Level) { + bit := uint32(1 << p.offset) + if l { + drvGPIOPL.gpioMemoryPL.data |= bit + } else { + drvGPIOPL.gpioMemoryPL.data &^= bit + } +} + +// PWM implements gpio.PinOut. +func (p *PinPL) PWM(gpio.Duty, physic.Frequency) error { + // TODO(maruel): PWM support for PL10. + return p.wrap(errors.New("not available on this CPU architecture")) +} + +// + +// function returns the current GPIO pin function. +// +// It must not be called if drvGPIOPL.gpioMemoryPL is nil. +func (p *PinPL) function() function { + shift := 4 * (p.offset % 8) + return function((drvGPIOPL.gpioMemoryPL.cfg[p.offset/8] >> shift) & 7) +} + +// setFunction changes the GPIO pin function. +// +// Returns false if the pin was in AltN. Only accepts in and out +// +// It must not be called if drvGPIOPL.gpioMemoryPL is nil. +func (p *PinPL) setFunction(f function) bool { + if f != in && f != out { + return false + } + // Interrupt based edge triggering is Alt5 but this is only supported on some + // pins. + // TODO(maruel): This check should use a whitelist of pins. + if actual := p.function(); actual != in && actual != out && actual != disabled && actual != alt5 { + // Pin is in special mode. + return false + } + off := p.offset / 8 + shift := 4 * (p.offset % 8) + mask := uint32(disabled) << shift + v := (uint32(f) << shift) ^ mask + // First disable, then setup. This is concurrent safe. + drvGPIOPL.gpioMemoryPL.cfg[off] |= mask + drvGPIOPL.gpioMemoryPL.cfg[off] &^= v + if p.function() != f { + panic(f) + } + return true +} + +func (p *PinPL) wrap(err error) error { + return fmt.Errorf("allwinner-gpio-pl (%s): %v", p, err) +} + +// + +// cpuPinsPL is all the pins as supported by the CPU. There is no guarantee that +// they are actually connected to anything on the board. +var cpuPinsPL = []PinPL{ + {offset: 0, name: "PL0", defaultPull: gpio.PullUp}, + {offset: 1, name: "PL1", defaultPull: gpio.PullUp}, + {offset: 2, name: "PL2", defaultPull: gpio.Float}, + {offset: 3, name: "PL3", defaultPull: gpio.Float}, + {offset: 4, name: "PL4", defaultPull: gpio.Float}, + {offset: 5, name: "PL5", defaultPull: gpio.Float}, + {offset: 6, name: "PL6", defaultPull: gpio.Float}, + {offset: 7, name: "PL7", defaultPull: gpio.Float}, + {offset: 8, name: "PL8", defaultPull: gpio.Float}, + {offset: 9, name: "PL9", defaultPull: gpio.Float}, + {offset: 10, name: "PL10", defaultPull: gpio.Float}, + {offset: 11, name: "PL11", defaultPull: gpio.Float}, + {offset: 12, name: "PL12", defaultPull: gpio.Float}, +} + +// See gpio.go for details. +var mappingPL = [13][5]pin.Func{ + {"RSB_SCK", "I2C_SCL", "", "", "PL_EINT0"}, // PL0 + {"RSB_SDA", "I2C_SDA", "", "", "PL_EINT1"}, // PL1 + {"UART_TX", "", "", "", "PL_EINT2"}, // PL2 + {"UART_RX", "", "", "", "PL_EINT3"}, // PL3 + {"JTAG_TMS", "", "", "", "PL_EINT4"}, // PL4 + {"JTAG_TCK", "", "", "", "PL_EINT5"}, // PL5 + {"JTAG_TDO", "", "", "", "PL_EINT6"}, // PL6 + {"JTAG_TDI", "", "", "", "PL_EINT7"}, // PL7 + {"I2C_SCL", "", "", "", "PL_EINT8"}, // PL8 + {"I2C_SDA", "", "", "", "PL_EINT9"}, // PL9 + {"PWM0", "", "", "", "PL_EINT10"}, // PL10 + {"CIR_RX", "", "", "", "PL_EINT11"}, // PL11 + {"", "", "", "", "PL_EINT12"}, // PL12 +} + +func init() { + PL0 = &cpuPinsPL[0] + PL1 = &cpuPinsPL[1] + PL2 = &cpuPinsPL[2] + PL3 = &cpuPinsPL[3] + PL4 = &cpuPinsPL[4] + PL5 = &cpuPinsPL[5] + PL6 = &cpuPinsPL[6] + PL7 = &cpuPinsPL[7] + PL8 = &cpuPinsPL[8] + PL9 = &cpuPinsPL[9] + PL10 = &cpuPinsPL[10] + PL11 = &cpuPinsPL[11] + PL12 = &cpuPinsPL[12] +} + +// getBaseAddressPL queries the virtual file system to retrieve the base address +// of the GPIO registers for GPIO pins in group PL. +// +// Defaults to 0x01F02C00 as per datasheet if could query the file system. +func getBaseAddressPL() uint64 { + base := uint64(0x01F02C00) + link, err := os.Readlink("/sys/bus/platform/drivers/sun50i-r-pinctrl/driver") + if err != nil { + return base + } + parts := strings.SplitN(path.Base(link), ".", 2) + if len(parts) != 2 { + return base + } + base2, err := strconv.ParseUint(parts[0], 16, 64) + if err != nil { + return base + } + return base2 +} + +// driverGPIOPL implements periph.Driver. +type driverGPIOPL struct { + // gpioMemoryPL is only the PL group in that case. Note that groups PI, PJ, PK + // do not exist. + gpioMemoryPL *gpioGroup +} + +func (d *driverGPIOPL) String() string { + return "allwinner-gpio-pl" +} + +func (d *driverGPIOPL) Prerequisites() []string { + return nil +} + +func (d *driverGPIOPL) After() []string { + return []string{"sysfs-gpio"} +} + +func (d *driverGPIOPL) Init() (bool, error) { + // BUG(maruel): H3 supports group PL too. + if !IsA64() { + return false, errors.New("A64 CPU not detected") + } + + // Mark the right pins as available even if the memory map fails so they can + // callback to sysfs.Pins. + functions := map[pin.Func]struct{}{} + for i := range cpuPinsPL { + name := cpuPinsPL[i].Name() + num := strconv.Itoa(cpuPinsPL[i].Number()) + cpuPinsPL[i].available = true + gpion := "GPIO" + num + + // Unregister the pin if already registered. This happens with sysfs-gpio. + // Do not error on it, since sysfs-gpio may have failed to load. + _ = gpioreg.Unregister(gpion) + _ = gpioreg.Unregister(num) + + // Register the pin with gpio. + if err := gpioreg.Register(&cpuPinsPL[i]); err != nil { + return true, err + } + if err := gpioreg.RegisterAlias(gpion, name); err != nil { + return true, err + } + if err := gpioreg.RegisterAlias(num, name); err != nil { + return true, err + } + switch f := cpuPinsPL[i].Func(); f { + case gpio.IN, gpio.OUT, pin.FuncNone: + default: + // Registering the same alias twice fails. This can happen if two pins + // are configured with the same function. + if _, ok := functions[f]; !ok { + // TODO(maruel): We'd have to clear out the ones from allwinner-gpio + // too. + functions[f] = struct{}{} + _ = gpioreg.RegisterAlias(string(f), name) + } + } + } + + // Now do a second loop but do the alternate functions. + for i := range cpuPinsPL { + for _, f := range cpuPinsPL[i].SupportedFuncs() { + switch f { + case gpio.IN, gpio.OUT: + default: + if _, ok := functions[f]; !ok { + // TODO(maruel): We'd have to clear out the ones from allwinner-gpio + // too. + functions[f] = struct{}{} + _ = gpioreg.RegisterAlias(string(f), cpuPinsPL[i].name) + } + } + } + } + + m, err := pmem.Map(getBaseAddressPL(), 4096) + if err != nil { + if os.IsPermission(err) { + return true, fmt.Errorf("need more access, try as root: %v", err) + } + return true, err + } + if err := m.AsPOD(&d.gpioMemoryPL); err != nil { + return true, err + } + + return true, nil +} + +func init() { + if isArm { + periph.MustRegister(&drvGPIOPL) + } +} + +var drvGPIOPL driverGPIOPL + +var _ gpio.PinIO = &PinPL{} +var _ gpio.PinIn = &PinPL{} +var _ gpio.PinOut = &PinPL{} +var _ pin.PinFunc = &PinPL{} diff --git a/vendor/periph.io/x/periph/host/allwinner/pwm.go b/vendor/periph.io/x/periph/host/allwinner/pwm.go new file mode 100644 index 0000000..92e0db6 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/pwm.go @@ -0,0 +1,197 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +import ( + "fmt" + "strings" + "time" +) + +const pwmClock = 24000000 +const pwmMaxPeriod = 0x10000 + +// prescalers is the value for pwm0Prescale* +var prescalers = []struct { + freq uint32 + scaler pwmPrescale +}{ + // Base frequency (min freq is half that) / PWM clock at pwmMaxPeriod + {pwmClock, pwmPrescale1}, // 24MHz / 366Hz + {pwmClock / 120, pwmPrescale120}, // 200kHz / 3Hz + {pwmClock / 180, pwmPrescale180}, // 133kHz / 2Hz + {pwmClock / 240, pwmPrescale240}, // 100kHz / 1.5Hz + {pwmClock / 360, pwmPrescale360}, // 66kHz / 1.01Hz + {pwmClock / 480, pwmPrescale480}, // 50kHz / 0.7Hz + {pwmClock / 12000, pwmPrescale12000}, // 2kHz + {pwmClock / 24000, pwmPrescale24000}, // 1kHz + {pwmClock / 36000, pwmPrescale36000}, // 666 Hz + {pwmClock / 48000, pwmPrescale48000}, // 500 Hz + {pwmClock / 72000, pwmPrescale72000}, // 333 Hz / 0.005Hz +} + +const ( + // 31:29 reserved + pwmBusy pwmCtl = 1 << 28 // PWM0_RDY + // 27:10 reserved (used for pwm1) + pwm0Mask pwmCtl = (1 << 10) - 1 + pwm0Bypass pwmCtl = 1 << 9 // PWM0_BYPASS (marked as unused on some drivers?) + pwm0PulseStart pwmCtl = 1 << 8 // PWM_CH0_PUL_START + pwm0ModePulse pwmCtl = 1 << 7 // PWM_CHANNEL0_MODE + pwm0SCLK pwmCtl = 1 << 6 // SCLK_CH0_GATING + pwm0Polarity pwmCtl = 1 << 5 // PWM_CH0_ACT_STA + pwm0Enable pwmCtl = 1 << 4 // PWM_CH0_EN + // 3:0 + pwm0PrescaleMask pwmCtl = pwmCtl(pwmPrescaleMask) // PWM_CH0_PRESCAL + pwm0Prescale120 pwmCtl = pwmCtl(pwmPrescale120) + pwm0Prescale180 pwmCtl = pwmCtl(pwmPrescale180) + pwm0Prescale240 pwmCtl = pwmCtl(pwmPrescale240) + pwm0Prescale360 pwmCtl = pwmCtl(pwmPrescale360) + pwm0Prescale480 pwmCtl = pwmCtl(pwmPrescale480) + // 5, 6, 7 reserved + pwm0Prescale12000 pwmCtl = pwmCtl(pwmPrescale12000) + pwm0Prescale24000 pwmCtl = pwmCtl(pwmPrescale24000) + pwm0Prescale36000 pwmCtl = pwmCtl(pwmPrescale36000) + pwm0Prescale48000 pwmCtl = pwmCtl(pwmPrescale48000) + pwm0Prescale72000 pwmCtl = pwmCtl(pwmPrescale72000) + // 13, 14 reserved + pwm0Prescale1 pwmCtl = pwmCtl(pwmPrescale1) +) + +// A64: Pages 194-195. +// R8: Pages 83-84. +type pwmCtl uint32 + +func (p pwmCtl) String() string { + var out []string + if p&pwmBusy != 0 { + out = append(out, "PWM0_RDY") + p &^= pwmBusy + } + if p&pwm0Bypass != 0 { + out = append(out, "PWM0_BYPASS") + p &^= pwm0Bypass + } + if p&pwm0PulseStart != 0 { + out = append(out, "PWM0_CH0_PUL_START") + p &^= pwm0PulseStart + } + if p&pwm0ModePulse != 0 { + out = append(out, "PWM0_CHANNEL0_MODE") + p &^= pwm0ModePulse + } + if p&pwm0SCLK != 0 { + out = append(out, "SCLK_CH0_GATING") + p &^= pwm0SCLK + } + if p&pwm0Polarity != 0 { + out = append(out, "PWM_CH0_ACT_STA") + p &^= pwm0Polarity + } + if p&pwm0Enable != 0 { + out = append(out, "PWM_CH0_EN") + p &^= pwm0Enable + } + out = append(out, pwmPrescale(p&pwm0PrescaleMask).String()) + p &^= pwm0PrescaleMask + if p != 0 { + out = append(out, fmt.Sprintf("Unknown(0x%08X)", uint32(p))) + } + return strings.Join(out, "|") +} + +const ( + pwmPrescaleMask pwmPrescale = 0xF + pwmPrescale120 pwmPrescale = 0 + pwmPrescale180 pwmPrescale = 1 + pwmPrescale240 pwmPrescale = 2 + pwmPrescale360 pwmPrescale = 3 + pwmPrescale480 pwmPrescale = 4 + // 5, 6, 7 reserved + pwmPrescale12000 pwmPrescale = 8 + pwmPrescale24000 pwmPrescale = 9 + pwmPrescale36000 pwmPrescale = 10 + pwmPrescale48000 pwmPrescale = 11 + pwmPrescale72000 pwmPrescale = 12 + // 13, 14 reserved + pwmPrescale1 pwmPrescale = 15 +) + +type pwmPrescale uint32 + +func (p pwmPrescale) String() string { + switch p { + case pwmPrescale120: + return "/120" + case pwmPrescale180: + return "/180" + case pwmPrescale240: + return "/240" + case pwmPrescale360: + return "/360" + case pwmPrescale480: + return "/480" + case pwmPrescale12000: + return "/12k" + case pwmPrescale24000: + return "/24k" + case pwmPrescale36000: + return "/36k" + case pwmPrescale48000: + return "/48k" + case pwmPrescale72000: + return "/72k" + case pwmPrescale1: + return "/1" + default: + return fmt.Sprintf("InvalidScalar(%d)", p&pwmPrescaleMask) + } +} + +// A64: Page 195. +// R8: Page 84 +type pwmPeriod uint32 + +func (p pwmPeriod) String() string { + return fmt.Sprintf("%d/%d", p&0xFFFF, uint32((p>>16)&0xFFFF)+1) +} + +func toPeriod(total uint32, active uint16) pwmPeriod { + if total > pwmMaxPeriod { + total = pwmMaxPeriod + } + return pwmPeriod(total-1)<<16 | pwmPeriod(active) +} + +// getBestPrescale finds the best prescaler. +// +// Cycles must be between 2 and 0x10000/2. +func getBestPrescale(period time.Duration) pwmPrescale { + // TODO(maruel): Rewrite this function, it is incorrect. + for _, v := range prescalers { + p := time.Second / time.Duration(v.freq) + smallest := (period / pwmMaxPeriod) + largest := (period / 2) + if p > smallest && p < largest { + return v.scaler + } + } + // Period is longer than 196s. + return pwmPrescale72000 +} + +// pwmMap represents the PWM memory mapped CPU registers. +// +// The base frequency is 24Mhz. +// +// TODO(maruel): Some CPU have 2 PWMs. +type pwmMap struct { + ctl pwmCtl // PWM_CTRL_REG + period pwmPeriod // PWM_CH0_PERIOD +} + +func (p *pwmMap) String() string { + return fmt.Sprintf("pwmMap{%s, %v}", p.ctl, p.period) +} diff --git a/vendor/periph.io/x/periph/host/allwinner/r8.go b/vendor/periph.io/x/periph/host/allwinner/r8.go new file mode 100644 index 0000000..7790763 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/r8.go @@ -0,0 +1,149 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// This file contains pin mapping information that is specific to the Allwinner +// R8 model. + +package allwinner + +import ( + "strings" + + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/sysfs" +) + +// R8 specific pins. +var ( + FEL *pin.BasicPin // Boot mode selection + MIC_IN *pin.BasicPin // Microphone in + MIC_GND *pin.BasicPin // Microphone ground + HP_LEFT *pin.BasicPin // Left speaker out + HP_RIGHT *pin.BasicPin // Right speaker out + HP_COM *pin.BasicPin // Speaker common + X1, X2, Y1, Y2 *pin.BasicPin // Touch screen pins +) + +// + +func init() { + FEL = &pin.BasicPin{N: "FEL"} + MIC_IN = &pin.BasicPin{N: "MIC_IN"} + MIC_GND = &pin.BasicPin{N: "MIC_GND"} + HP_LEFT = &pin.BasicPin{N: "HP_LEFT"} + HP_RIGHT = &pin.BasicPin{N: "HP_RIGHT"} + HP_COM = &pin.BasicPin{N: "HP_COM"} + + X1 = &pin.BasicPin{N: "X1"} + X2 = &pin.BasicPin{N: "X2"} + Y1 = &pin.BasicPin{N: "Y1"} + Y2 = &pin.BasicPin{N: "Y2"} +} + +// mappingR8 describes the mapping of each R8 processor gpio to their alternate +// functions. +// +// It omits the in & out functions which are available on all pins. +// +// The mapping comes from the datasheet page 18: +// https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20Datasheet%20V1.2.pdf +// +// - The datasheet uses TWI instead of I2C but this is renamed here for consistency. +var mappingR8 = map[string][5]pin.Func{ + "PB0": {"I2C0_SCL"}, + "PB1": {"I2C0_SDA"}, + "PB2": {"PWM0", "", "", "", "EINT16"}, + "PB3": {"IR_TX", "", "", "", "EINT17"}, + "PB4": {"IR_RX", "", "", "", "EINT18"}, + "PB10": {"SPI2_CS1"}, + "PB15": {"I2C1_SCL"}, + "PB16": {"I2C1_SDA"}, + "PB17": {"I2C2_SCL"}, + "PB18": {"I2C2_SDA"}, + "PC0": {"NAND_WE", "SPI0_MOSI"}, + "PC1": {"NAND_ALE", "SPI0_MISO"}, + "PC2": {"NAND_CLE", "SPI0_CLK"}, + "PC3": {"NAND_CE1", "SPI0_CS0"}, + "PC4": {"NAND_CE0"}, + "PC5": {"NAND_RE"}, + "PC6": {"NAND_RB0", "SDC2_CMD"}, + "PC7": {"NAND_RB1", "SDC2_CLK"}, + "PC8": {"NAND_DQ0", "SDC2_D0"}, + "PC9": {"NAND_DQ1", "SDC2_D1"}, + "PC10": {"NAND_DQ2", "SDC2_D2"}, + "PC11": {"NAND_DQ3", "SDC2_D3"}, + "PC12": {"NAND_DQ4", "SDC2_D4"}, + "PC13": {"NAND_DQ5", "SDC2_D5"}, + "PC14": {"NAND_DQ6", "SDC2_D6"}, + "PC15": {"NAND_DQ7", "SDC2_D7"}, + "PC19": {""}, + "PD2": {"LCD_D2", "UART2_TX"}, + "PD3": {"LCD_D3", "UART2_RX"}, + "PD4": {"LCD_D4", "UART2_CTX"}, + "PD5": {"LCD_D5", "UART2_RTS"}, + "PD6": {"LCD_D6", "ECRS"}, + "PD7": {"LCD_D7", "ECOL"}, + "PD10": {"LCD_D10", "ERXD0"}, + "PD11": {"LCD_D11", "ERXD1"}, + "PD12": {"LCD_D12", "ERXD2"}, + "PD13": {"LCD_D13", "ERXD3"}, + "PD14": {"LCD_D14", "ERXCK"}, + "PD15": {"LCD_D15", "ERXERR"}, + "PD18": {"LCD_D18", "ERXDV"}, + "PD19": {"LCD_D19", "ETXD0"}, + "PD20": {"LCD_D20", "ETXD1"}, + "PD21": {"LCD_D21", "ETXD2"}, + "PD22": {"LCD_D22", "ETXD3"}, + "PD23": {"LCD_D23", "ETXEN"}, + "PD24": {"LCD_CLK", "ETXCK"}, + "PD25": {"LCD_DE", "ETXERR"}, + "PD26": {"LCD_HSYNC", "EMDC"}, + "PD27": {"LCD_VSYNC", "EMDIO"}, + "PE0": {"TS_CLK", "CSI_PCLK", "SPI2_CS0", "", "EINT14"}, + "PE1": {"TS_ERR", "CSI_MCLK", "SPI2_CLK", "", "EINT15"}, + "PE2": {"TS_SYNC", "CSI_HSYNC", "SPI2_MOSI"}, + "PE3": {"TS_DVLD", "CSI_VSYNC", "SPI2_MISO"}, + "PE4": {"TS_D0", "CSI_D0", "SDC2_D0"}, + "PE5": {"TS_D1", "CSI_D1", "SDC2_D1"}, + "PE6": {"TS_D2", "CSI_D2", "SDC2_D2"}, + "PE7": {"TS_D3", "CSI_D3", "SDC2_D3"}, + "PE8": {"TS_D4", "CSI_D4", "SDC2_CMD"}, + "PE9": {"TS_D5", "CSI_D5", "SDC2_CLK"}, + "PE10": {"TS_D6", "CSI_D6", "UART1_TX"}, + "PE11": {"TS_D7", "CSI_D7", "UART1_RX"}, + "PF0": {"SDC0_D1", "", "JTAG1_TMS"}, + "PF1": {"SDC0_D0", "", "JTAG1_TDI"}, + "PF2": {"SDC0_CLK", "", "UART0_TX"}, + "PF3": {"SDC0_CMD", "", "JTAG1_TDO"}, + "PF4": {"SDC0_D3", "", "UART0_RX"}, + "PF5": {"SDC0_D2", "", "JTAG1_TCK"}, + "PG0": {"GPS_CLK", "", "", "", "EINT0"}, + "PG1": {"GPS_SIGN", "", "", "", "EINT1"}, + "PG2": {"GPS_MAG", "", "", "", "EINT2"}, + "PG3": {"", "", "UART1_TX", "", "EINT3"}, + "PG4": {"", "", "UART1_RX", "", "EINT4"}, + "PG9": {"SPI1_CS0", "UART3_TX", "", "", "EINT9"}, + "PG10": {"SPI1_CLK", "UART3_RX", "", "", "EINT10"}, + "PG11": {"SPI1_MOSI", "UART3_CTS", "", "", "EINT11"}, + "PG12": {"SPI1_MISO", "UART3_RTS", "", "", "EINT12"}, +} + +// mapR8Pins uses mappingR8 to actually set the altFunc fields of all gpio and +// mark them as available. +// +// It is called by the generic allwinner processor code if a R8 is detected. +func mapR8Pins() error { + for name, altFuncs := range mappingR8 { + pin := cpupins[name] + pin.altFunc = altFuncs + pin.available = true + if strings.Contains(string(altFuncs[4]), "EINT") { + pin.supportEdge = true + } + + // Initializes the sysfs corresponding pin right away. + pin.sysfsPin = sysfs.Pins[pin.Number()] + } + return nil +} diff --git a/vendor/periph.io/x/periph/host/allwinner/spi.go b/vendor/periph.io/x/periph/host/allwinner/spi.go new file mode 100644 index 0000000..ceb645e --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/spi.go @@ -0,0 +1,207 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +import ( + "errors" + "fmt" + "log" + "time" +) + +const ( + // 31:20 reserved + // Set this bit to ‘1’ to make the internal read sample point with a delay of + // half cycle of SPI_CLK. It is used in high speed read operation to reduce + // the error caused by the time delay of SPI_CLK propagating between master + // and slave. + // 1 – delay internal read sample point + // 0 – normal operation, do not delay internal read sample point + spiR8HalfDelay spiR8Ctl = 1 << 19 // Master Sample Data Control + spiR8TransmitPause spiR8Ctl = 1 << 18 // Transmit Pause Enable + spiR8CSLevel spiR8Ctl = 1 << 17 // SS_LEVEL; Chip Select level + spiR8CSManual spiR8Ctl = 1 << 16 // SS_CTRL; Do not switch CS automatically + spiR8DiscardHash spiR8Ctl = 1 << 15 // DHB + spiR8DummyBurst spiR8Ctl = 1 << 14 // DDB + spiR8CS0 spiR8Ctl = 0 << 12 // SS; Which CS line to use. For SPI0 only + spiR8CS1 spiR8Ctl = 1 << 12 // + spiR8CS2 spiR8Ctl = 2 << 12 // + spiR8CS3 spiR8Ctl = 3 << 12 // + spiR8RapidsReadMode spiR8Ctl = 1 << 11 // RPSM + spiR8ExchangeBurst spiR8Ctl = 1 << 10 // XCH + spiR8RXFIFOReset spiR8Ctl = 1 << 9 // RXFIFO Reset; Write to reset the FIFO as empty + spiR8TXFIFOReset spiR8Ctl = 1 << 8 // TXFIFO Reset; Write to reset the FIFO as empty + spiR8CSBetweenBursts spiR8Ctl = 1 << 7 // SSCTL + spiR8LSB spiR8Ctl = 1 << 6 // LMTF; MSB by default, LSB when set + spiR8DDMA spiR8Ctl = 1 << 5 // DMAM; Use dedicated DMA if set, normal DMA otherwise + spiR8CSActiveLow spiR8Ctl = 1 << 4 // SSPOL; CS line polarity + spiR8ClkActiveLow spiR8Ctl = 1 << 3 // POL; Clock line polarity + spiR8PHA spiR8Ctl = 1 << 2 // PHA; Phase 1 if set (leading edge for setup data) + spiR8Master spiR8Ctl = 1 << 1 // MODE; Slave mode if not set + spiR8Enable spiR8Ctl = 1 << 0 // EN; Enable mode +) + +// SPI_CTL +// R8: Page 153-155. Default: 0x0002001C +type spiR8Ctl uint32 + +// SPI_INTCTL +// R8: Page 155-156. +type spiR8IntCtl uint32 + +const ( + spiR8ClearInterrupt spiR8IntStatus = 1 << 31 // Clear interrupt busy flag + // 30:18 reserved + spiR8InvalidSS spiR8IntStatus = 1 << 17 // SSI + spiR8TC spiR8IntStatus = 1 << 16 // TC; Transfer Completed +) + +// SPI_INT_STA +// R8: Page 156-157. +type spiR8IntStatus uint32 + +const ( + // 31:13 reserved + spiR8DMATX3Quarter spiR8DMACtl = 1 << 12 // TXFIFO 3/4 empty + spiR8DMATX1Quarter spiR8DMACtl = 1 << 11 // TXFIFO 1/4 empty + spiR8DMATXByte spiR8DMACtl = 1 << 10 // TXFIFO Not Full + spiR8DMATXHalf spiR8DMACtl = 1 << 9 // TXFIFO 1/2 empty + spiR8DMATXEmpty spiR8DMACtl = 1 << 8 // TXFIFO empty + // 7:5 reserved + spiR8DMARX3Quarter spiR8DMACtl = 1 << 4 // RXFIFO 3/4 empty + spiR8DMARX1Quarter spiR8DMACtl = 1 << 3 // RXFIFO 1/4 empty + spiR8DMARXByte spiR8DMACtl = 1 << 2 // RXFIFO Not Full + spiR8DMARXHalf spiR8DMACtl = 1 << 1 // RXFIFO 1/2 empty + spiR8DMARXEmpty spiR8DMACtl = 1 << 0 // RXFIFO empty +) + +// SPI_DMACTL +// R8: Page 158. +type spiR8DMACtl uint32 + +const ( + // 31:13 reserved + spiR8DivRateSelect2 spiR8ClockCtl = 1 << 12 // DRS; Use spiDivXX if set, use mask otherwise + spiR8Div2 spiR8ClockCtl = 0 << 8 // CDR1; Use divisor 2^(n+1) + spiR8Div4 spiR8ClockCtl = 1 << 8 // + spiR8Div8 spiR8ClockCtl = 2 << 8 // + spiR8Div16 spiR8ClockCtl = 3 << 8 // + spiR8Div32 spiR8ClockCtl = 4 << 8 // + spiR8Div64 spiR8ClockCtl = 5 << 8 // + spiR8Div128 spiR8ClockCtl = 6 << 8 // + spiR8Div256 spiR8ClockCtl = 7 << 8 // + spiR8Div512 spiR8ClockCtl = 8 << 8 // + spiR8Div1024 spiR8ClockCtl = 9 << 8 // + spiR8Div2048 spiR8ClockCtl = 10 << 8 // + spiR8Div4096 spiR8ClockCtl = 11 << 8 // + spiR8Div8192 spiR8ClockCtl = 12 << 8 // + spiR8Div16384 spiR8ClockCtl = 13 << 8 // + spiR8Div32768 spiR8ClockCtl = 14 << 8 // + spiR8Div65536 spiR8ClockCtl = 15 << 8 // + spiR8Div1Mask spiR8ClockCtl = 0xFF // CDR2; Use divisor 2*(n+1) +) + +// SPI_CCTL +// R8: Page 159. +type spiR8ClockCtl uint32 + +const ( + // 31:25 reserved + spiR8FIFOTXShift = 16 // 0 to 64 + // 15:7 reserved + spiR8FIFORXShift = 0 // 0 to 64 +) + +// SPI_FIFO_STA +// R8: Page 160. +type spiR8FIFOStatus uint32 + +func (s spiR8FIFOStatus) tx() uint8 { + return uint8((uint32(s) >> 16) & 127) +} + +func (s spiR8FIFOStatus) rx() uint8 { + return uint8(uint32(s) & 127) +} + +// spiR8Group is the mapping of SPI registers for one SPI controller. +// R8: Page 152-153. +type spiR8Group struct { + rx uint32 // 0x00 SPI_RX_DATA RX Data + tx uint32 // 0x04 SPI_TX_DATA TX Data + ctl spiR8Ctl // 0x08 SPI_CTL Control + intCtl spiR8IntCtl // 0x0C SPI_INTCTL Interrupt Control + status spiR8IntStatus // 0x10 SPI_ST Status + dmaCtl spiR8DMACtl // 0x14 SPI_DMACTL DMA Control + wait uint32 // 0x18 SPI_WAIT Clock Counter; 16 bits + clockCtl spiR8ClockCtl // 0x1C SPI_CCTL Clock Rate Control + burstCounter uint32 // 0x20 SPI_BC Burst Counter; 24 bits + transmitCounter uint32 // 0x24 SPI_TC Transmit Counter; 24 bits + fifoStatus spiR8FIFOStatus // 0x28 SPI_FIFO_STA FIFO Status + reserved [(0x1000 - 0x02C) / 4]uint32 +} + +func (s *spiR8Group) setup() { + s.intCtl = 0 + s.status = 0 + //s.dmaCtl = spiR8DMARXByte + s.dmaCtl = 0 + s.wait = 2 + s.clockCtl = spiR8DivRateSelect2 | spiR8Div1024 + // spiR8DDMA + s.ctl = spiR8CSManual | spiR8LSB | spiR8Master | spiR8Enable +} + +// spiMap is the mapping of SPI registers. +// R8: Page 152-153. +type spiMap struct { + groups [3]spiR8Group +} + +// spi2Write do a write on SPI2_MOSI via polling. +func spi2Write(w []byte) error { + if drvDMA.clockMemory == nil || drvDMA.spiMemory == nil { + return errors.New("subsystem not initialized") + } + // Make sure the source clock is disabled. Set it at 250kHz. + //drvDMA.clockMemory.spi2Clk &^= clockSPIEnable + drvDMA.clockMemory.spi2Clk |= clockSPIEnable + drvDMA.clockMemory.spi2Clk = clockSPIDiv8a | clockSPIDiv12b + ch := &drvDMA.spiMemory.groups[2] + ch.setup() + fmt.Printf("Setup done\n") + for i := 0; i < len(w)/4; i++ { + // TODO(maruel): Access it in 8bit mode. + ch.tx = uint32(w[0]) + for ch.fifoStatus.tx() == 0 { + log.Printf("Waiting for bit %# v\n", ch) + time.Sleep(time.Second) + } + } + fmt.Printf("Done\n") + return nil +} + +// spi2Read do a read on SPI2_MISO via polling. +func spi2Read(r []byte) error { + if drvDMA.clockMemory == nil || drvDMA.spiMemory == nil { + return errors.New("subsystem not initialized") + } + // Make sure the source clock is disabled. Set it at 250kHz. + //drvDMA.clockMemory.spi2Clk &^= clockSPIEnable + drvDMA.clockMemory.spi2Clk |= clockSPIEnable + drvDMA.clockMemory.spi2Clk = clockSPIDiv8a | clockSPIDiv12b + ch := &drvDMA.spiMemory.groups[2] + ch.setup() + for i := 0; i < len(r)/4; i++ { + ch.tx = 0 + for ch.status&spiR8TC == 0 { + } + // TODO(maruel): Access it in 8bit mode. + r[i] = uint8(ch.rx) + } + fmt.Printf("Done\n") + return nil +} diff --git a/vendor/periph.io/x/periph/host/allwinner/timer.go b/vendor/periph.io/x/periph/host/allwinner/timer.go new file mode 100644 index 0000000..f11f805 --- /dev/null +++ b/vendor/periph.io/x/periph/host/allwinner/timer.go @@ -0,0 +1,128 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +import ( + "time" + + "periph.io/x/periph/host/cpu" +) + +// ReadTime returns the time on a monotonic timer. +// +// It only works if allwinner-dma successfully loaded. Otherwise it returns 0. +func ReadTime() time.Duration { + if drvDMA.timerMemory == nil { + return 0 + } + v := uint64(drvDMA.timerMemory.counterHigh)<<32 | uint64(drvDMA.timerMemory.counterLow) + if v == 0 { + // BUG(maruel): Implement using AVS_CNT0_REG on A64. + return 0 + } + // BUG(maruel): Assumes that counterCtrl & timerPLL6 is not set. + const tick = time.Microsecond / 24 + return time.Duration(v) * tick +} + +// Nanospin spins the CPU without calling into the kernel code if possible. +func Nanospin(t time.Duration) { + start := ReadTime() + if start == 0 { + // Use the slow generic version. + cpu.Nanospin(t) + return + } + for ReadTime()-start < t { + } +} + +// + +const ( + // 31:3 reserved + timerPLL6 timerCtrl = 2 << 1 // CONT64_CLK_SRC_SEL; OSC24M if not set; + timerReadLatchEnable timerCtrl = 1 << 1 // CONT64_RLATCH_EN; 1 to latch the counter to the registers + timerClear = 1 << 0 // CONT64_CLR_EN; clears the counter +) + +// R8: Page 96 +type timerCtrl uint32 + +// timerMap is the mapping of important registers across CPUs. +type timerMap struct { + reserved0 [0x80 / 4]uint32 // + cntCtl timerCtrl // 0x80 AVS_CNT_CTL_REG AVS Control Register + cnt0 uint32 // 0x84 AVS_CNT0_REG AVS Counter 0 Register + cnt1 uint32 // 0x88 AVS_CNT1_REG AVS Counter 1 Register + cndDrv uint32 // 0x8C AVS_CNT_DIV_REG AVS Divisor Register + reserved1 [0x10 / 4]uint32 // On R8 only. + counterCtrl timerCtrl // 0x0A0 COUNTER64_CTRL_REG 64-bit Counter control + counterLow uint32 // 0x0A4 COUNTER64_LOW_REG 64-bit Counter low + counterHigh uint32 // 0x0A8 COUNTER64_HI_REG 64-bit Counter high +} + +// A64: Page 161. +type timerMapA64 struct { + reserved0 uint32 // 0x0 TMR_IRQ_EN_REG Timer IRQ Enable Register + reserved1 uint32 // 0x4 TMR_IRQ_STA_REG Timer Status Register + reserved2 uint32 // 0x10 TMR0_CTRL_REG Timer 0 Control Register + reserved3 uint32 // 0x14 TMR0_INTV_VALUE_REG Timer 0 Interval Value Register + reserved4 uint32 // 0x18 TMR0_CUR_VALUE_REG Timer 0 Current Value Register + reserved5 uint32 // 0x20 TMR1_CTRL_REG Timer 1 Control Register + reserved6 uint32 // 0x24 TMR1_INTV_VALUE_REG Timer 1 Interval Value Register + reserved7 uint32 // 0x28 TMR1_CUR_VALUE_REG Timer 1 Current Value Register + cntCtl timerCtrl // 0x80 AVS_CNT_CTL_REG AVS Control Register + cnt0 uint32 // 0x84 AVS_CNT0_REG AVS Counter 0 Register + cnt1 uint32 // 0x88 AVS_CNT1_REG AVS Counter 1 Register + cndDrv uint32 // 0x8C AVS_CNT_DIV_REG AVS Divisor Register + reserved8 uint32 // 0xA0 WDOG0_IRQ_EN_REG Watchdog 0 IRQ Enable Register + reserved9 uint32 // 0xA4 WDOG0_IRQ_STA_REG Watchdog 0 Status Register + reserved10 uint32 // 0xB0 WDOG0_CTRL_REG Watchdog 0 Control Register + reserved11 uint32 // 0xB4 WDOG0_CFG_REG Watchdog 0 Configuration Register + reserved12 uint32 // 0xB8 WDOG0_MODE_REG Watchdog 0 Mode Register +} + +// R8: Page 85 +type timerMapR8 struct { + reserved0 uint32 // 0x000 ASYNC_TMR_IRQ_EN_REG Timer IRQ Enable + reserved1 uint32 // 0x004 ASYNC_TMR_IRQ_STAS_REG Timer Status + reserved2 [2]uint32 // 0x008-0x00C + reserved3 uint32 // 0x010 ASYNC_TMR0_CTRL_REG Timer 0 Control + reserved4 uint32 // 0x014 ASYNC_TMR0_INTV_VALUE_REG Timer 0 Interval Value + reserved5 uint32 // 0x018 ASYNC_TMR0_CURNT_VALUE_REG Timer 0 Current Value + reserved6 uint32 // 0x01C + reserved7 uint32 // 0x020 ASYNC_TMR1_CTRL_REG Timer 1 Control + reserved8 uint32 // 0x024 ASYNC_TMR1_INTV_VALUE_REG Timer 1 Interval Value + reserved9 uint32 // 0x028 ASYNC_TMR1_CURNT_VALUE_REG Timer 1 Current Value + reserved10 uint32 // 0x02C + reserved11 uint32 // 0x030 ASYNC_TMR2_CTRL_REG Timer 2 Control + reserved12 uint32 // 0x034 ASYNC_TMR2_INTV_VALUE_REG Timer 2 Interval Value + reserved13 uint32 // 0x038 ASYNC_TMR2_CURNT_VALUE_REG Timer 2 Current Value + reserved14 uint32 // 0x03C + reserved15 uint32 // 0x040 ASYNC_TMR3_CTRL_REG Timer 3 Control + reserved16 uint32 // 0x044 ASYNC_TMR3_INTV_VALUE_REG Timer 3 Interval Value + reserved17 [2]uint32 // 0x048-0x04C + reserved18 uint32 // 0x050 ASYNC_TMR4_CTRL_REG Timer 4 Control + reserved19 uint32 // 0x054 ASYNC_TMR4_INTV_VALUE_REG Timer 4 Interval Value + reserved20 uint32 // 0x058 ASYNC_TMR4_CURNT_VALUE_REG Timer 4 Current Value + reserved21 uint32 // 0x05C + reserved22 uint32 // 0x060 ASYNC_TMR5_CTRL_REG Timer 5 Control + reserved23 uint32 // 0x064 ASYNC_TMR5_INTV_VALUE_REG Timer 5 Interval Value + reserved24 uint32 // 0x068 ASYNC_TMR5_CURNT_VALUE_REG Timer 5 Current Value + reserved25 [5]uint32 // 0x06C-0x07C + cntCtl timerCtrl // 0x080 AVS_CNT_CTL_REG AVS Control Register + cnt0 uint32 // 0x084 AVS_CNT0_REG AVS Counter 0 Register + cnt1 uint32 // 0x088 AVS_CNT1_REG AVS Counter 1 Register + cndDiv uint32 // 0x08C AVS_CNT_DIVISOR_REG AVS Divisor + reserved26 uint32 // 0x090 WDOG_CTRL_REG + reserved27 uint32 // 0x094 WDOG_MODE_REG Watchdog Mode + reserved28 [2]uint32 // 0x098-0x09C + counterCtrl timerCtrl // 0x0A0 COUNTER64_CTRL_REG 64-bit Counter control + counterLow uint32 // 0x0A4 COUNTER64_LOW_REG 64-bit Counter low + counterHigh uint32 // 0x0A8 COUNTER64_HI_REG 64-bit Counter high + reserved29 [0x94]uint32 // 0x0AC-0x13C + reserved30 uint32 // 0x140 CPU_CFG_REG CPU configuration register +} diff --git a/vendor/periph.io/x/periph/host/am335x/am335x.go b/vendor/periph.io/x/periph/host/am335x/am335x.go new file mode 100644 index 0000000..686ce25 --- /dev/null +++ b/vendor/periph.io/x/periph/host/am335x/am335x.go @@ -0,0 +1,52 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package am335x + +import ( + "errors" + "strings" + + "periph.io/x/periph" + "periph.io/x/periph/host/distro" +) + +// Present returns true if a TM AM335x processor is detected. +func Present() bool { + if isArm { + return strings.HasPrefix(distro.DTModel(), "TI AM335x") + } + return false +} + +// driver implements periph.Driver. +type driver struct { +} + +func (d *driver) String() string { + return "am335x" +} + +func (d *driver) Prerequisites() []string { + return nil +} + +func (d *driver) After() []string { + return nil +} + +func (d *driver) Init() (bool, error) { + if !Present() { + return false, errors.New("am335x CPU not detected") + } + return true, nil +} + +func init() { + if isArm { + periph.MustRegister(&drv) + } +} + +var drv driver diff --git a/vendor/periph.io/x/periph/host/am335x/am335x_arm.go b/vendor/periph.io/x/periph/host/am335x/am335x_arm.go new file mode 100644 index 0000000..8d17758 --- /dev/null +++ b/vendor/periph.io/x/periph/host/am335x/am335x_arm.go @@ -0,0 +1,7 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package am335x + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/am335x/am335x_other.go b/vendor/periph.io/x/periph/host/am335x/am335x_other.go new file mode 100644 index 0000000..3f3dbea --- /dev/null +++ b/vendor/periph.io/x/periph/host/am335x/am335x_other.go @@ -0,0 +1,9 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm + +package am335x + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/am335x/doc.go b/vendor/periph.io/x/periph/host/am335x/doc.go new file mode 100644 index 0000000..2967405 --- /dev/null +++ b/vendor/periph.io/x/periph/host/am335x/doc.go @@ -0,0 +1,28 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package am335x exposes functionality for the Texas Instruments Sitara AM335x +// processor family. +// +// This processor family is found on the BeagleBone. PRU-ICSS functionality is +// implemented in package pru. +// +// The GPIO pins of the AM335x CPU are grouped into 3 groups of 32 pins: GPIO0, +// GPIO1, and GPIO2. The CPU documentation refers to GPIO in the form of +// GPIOx_y. To get the absolute number, as exposed by sysfs, use 32*x+y to get +// the absolute number. +// +// Datasheet +// +// Technical Reference Manual +// https://www.ti.com/lit/ug/spruh73p/spruh73p.pdf +// +// Other +// +// Marketing page +// https://www.ti.com/processors/sitara/arm-cortex-a8/am335x/overview.html +// +// Family overview +// https://www.ti.com/lit/ds/symlink/am3359.pdf +package am335x diff --git a/vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm.go b/vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm.go new file mode 100644 index 0000000..f8c1f6c --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package bcm283x + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm64.go b/vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm64.go new file mode 100644 index 0000000..a9bf6a3 --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/bcm283x_arm64.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build arm64 + +package bcm283x + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/bcm283x/bcm283x_other.go b/vendor/periph.io/x/periph/host/bcm283x/bcm283x_other.go new file mode 100644 index 0000000..6a122b3 --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/bcm283x_other.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm,!arm64 + +package bcm283x + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/bcm283x/clock.go b/vendor/periph.io/x/periph/host/bcm283x/clock.go new file mode 100644 index 0000000..f1f084c --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/clock.go @@ -0,0 +1,330 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package bcm283x + +import ( + "errors" + "fmt" + "strings" + "time" + + "periph.io/x/periph/conn/physic" +) + +// errClockRegister is returned in a situation where the clock memory is not +// working as expected. It is mocked in tests. +var errClockRegister = errors.New("can't write to clock divisor CPU register") + +// Clock sources frequency in hertz. +const ( + clk19dot2MHz = 19200 * physic.KiloHertz + clk500MHz = 500 * physic.MegaHertz +) + +const ( + // 31:24 password + clockPasswdCtl clockCtl = 0x5A << 24 // PASSWD + // 23:11 reserved + clockMashMask clockCtl = 3 << 9 // MASH + clockMash0 clockCtl = 0 << 9 // src_freq / divI (ignores divF) + clockMash1 clockCtl = 1 << 9 + clockMash2 clockCtl = 2 << 9 + clockMash3 clockCtl = 3 << 9 // will cause higher spread + clockFlip clockCtl = 1 << 8 // FLIP + clockBusy clockCtl = 1 << 7 // BUSY + // 6 reserved + clockKill clockCtl = 1 << 5 // KILL + clockEnable clockCtl = 1 << 4 // ENAB + clockSrcMask clockCtl = 0xF << 0 // SRC + clockSrcGND clockCtl = 0 // 0Hz + clockSrc19dot2MHz clockCtl = 1 // 19.2MHz + clockSrcTestDebug0 clockCtl = 2 // 0Hz + clockSrcTestDebug1 clockCtl = 3 // 0Hz + clockSrcPLLA clockCtl = 4 // 0Hz + clockSrcPLLC clockCtl = 5 // 1000MHz (changes with overclock settings) + clockSrcPLLD clockCtl = 6 // 500MHz + clockSrcHDMI clockCtl = 7 // 216MHz; may be disabled + // 8-15 == GND. +) + +// clockCtl controls the clock properties. +// +// It must not be changed while busy is set or a glitch may occur. +// +// Page 107 +type clockCtl uint32 + +func (c clockCtl) String() string { + var out []string + if c&0xFF000000 == clockPasswdCtl { + c &^= 0xFF000000 + out = append(out, "PWD") + } + switch c & clockMashMask { + case clockMash1: + out = append(out, "Mash1") + case clockMash2: + out = append(out, "Mash2") + case clockMash3: + out = append(out, "Mash3") + default: + } + c &^= clockMashMask + if c&clockFlip != 0 { + out = append(out, "Flip") + c &^= clockFlip + } + if c&clockBusy != 0 { + out = append(out, "Busy") + c &^= clockBusy + } + if c&clockKill != 0 { + out = append(out, "Kill") + c &^= clockKill + } + if c&clockEnable != 0 { + out = append(out, "Enable") + c &^= clockEnable + } + switch x := c & clockSrcMask; x { + case clockSrcGND: + out = append(out, "GND(0Hz)") + case clockSrc19dot2MHz: + out = append(out, "19.2MHz") + case clockSrcTestDebug0: + out = append(out, "Debug0(0Hz)") + case clockSrcTestDebug1: + out = append(out, "Debug1(0Hz)") + case clockSrcPLLA: + out = append(out, "PLLA(0Hz)") + case clockSrcPLLC: + out = append(out, "PLLD(1000MHz)") + case clockSrcPLLD: + out = append(out, "PLLD(500MHz)") + case clockSrcHDMI: + out = append(out, "HDMI(216MHz)") + default: + out = append(out, fmt.Sprintf("GND(%d)", x)) + } + c &^= clockSrcMask + if c != 0 { + out = append(out, fmt.Sprintf("clockCtl(0x%0x)", uint32(c))) + } + return strings.Join(out, "|") +} + +const ( + // 31:24 password + clockPasswdDiv clockDiv = 0x5A << 24 // PASSWD + // Integer part of the divisor + clockDiviShift = 12 + clockDiviMax = (1 << 12) - 1 + clockDiviMask clockDiv = clockDiviMax << clockDiviShift // DIVI + // Fractional part of the divisor + clockDivfMask clockDiv = (1 << 12) - 1 // DIVF +) + +// clockDiv is a 12.12 fixed point value. +// +// The fractional part generates a significant amount of noise so it is +// preferable to not use it. +// +// Page 108 +type clockDiv uint32 + +func (c clockDiv) String() string { + i := (c & clockDiviMask) >> clockDiviShift + c &^= clockDiviMask + if c == 0 { + return fmt.Sprintf("%d.0", i) + } + return fmt.Sprintf("%d.(%d/%d)", i, c, clockDiviMax) +} + +// clock is a pair of clockCtl / clockDiv. +// +// It can be set to one of the sources: clockSrc19dot2MHz(19.2MHz) and +// clockSrcPLLD(500Mhz), then divided to a value to get the resulting clock. +// Per spec the resulting frequency should be under 25Mhz. +type clock struct { + ctl clockCtl + div clockDiv +} + +// findDivisorExact finds the clock divisor and wait cycles to reduce src to +// desired hz. +// +// The clock divisor is capped to clockDiviMax. +// +// Returns clock divisor, wait cycles. Returns 0, 0 if no exact match is found. +// Favorizes high clock divisor value over high clock wait cycles. This means +// that the function is slower than it could be, but results in more stable +// clock. +func findDivisorExact(src, desired physic.Frequency, maxWaitCycles uint32) (uint32, uint32) { + if src < desired || src%desired != 0 || src/physic.Frequency(maxWaitCycles*clockDiviMax) > desired { + // Can't attain without oversampling (too low) or desired frequency is + // higher than the source (too high) or is not a multiple. + return 0, 0 + } + factor := uint32(src / desired) + // TODO(maruel): Only iterate over valid divisors to save a bit more + // calculations. Since it's is only doing 32 loops, this is not a big deal. + for wait := uint32(1); wait <= maxWaitCycles; wait++ { + if rest := factor % wait; rest != 0 { + continue + } + clk := factor / wait + if clk == 0 { + break + } + if clk <= clockDiviMax { + return clk, wait + } + } + return 0, 0 +} + +// findDivisorOversampled tries to find the lowest allowed oversampling to make +// desiredHz a multiple of srcHz. +// +// Allowed oversampling depends on the desiredHz. Cap oversampling because +// oversampling at 10x in the 1Mhz range becomes unreasonable in term of +// memory usage. +func findDivisorOversampled(src, desired physic.Frequency, maxWaitCycles uint32) (uint32, uint32, physic.Frequency) { + //log.Printf("findDivisorOversampled(%s, %s, %d)", src, desired, maxWaitCycles) + // There are 2 reasons: + // - desired is so low it is not possible to lower src to this frequency + // - not a multiple, there's a need for a prime number + // TODO(maruel): Rewrite without a loop, this is not needed. Leverage primes + // to reduce the number of iterations. + for multiple := physic.Frequency(2); ; multiple++ { + n := multiple * desired + if n > 100*physic.KiloHertz && multiple > 10 { + break + } + if clk, wait := findDivisorExact(src, n, maxWaitCycles); clk != 0 { + return clk, wait, n + } + } + return 0, 0, 0 +} + +// calcSource choose the best source to get the exact desired clock. +// +// It calculates the clock source, the clock divisor and the wait cycles, if +// applicable. Wait cycles is 'div minus 1'. +func calcSource(f physic.Frequency, maxWaitCycles uint32) (clockCtl, uint32, uint32, physic.Frequency, error) { + if f < physic.Hertz { + return 0, 0, 0, 0, fmt.Errorf("bcm283x-clock: desired frequency %s must be >1hz", f) + } + if f > 125*physic.MegaHertz { + return 0, 0, 0, 0, fmt.Errorf("bcm283x-clock: desired frequency %s is too high", f) + } + // http://elinux.org/BCM2835_datasheet_errata states that clockSrc19dot2MHz + // is the cleanest clock source so try it first. + div, wait := findDivisorExact(clk19dot2MHz, f, maxWaitCycles) + if div != 0 { + return clockSrc19dot2MHz, div, wait, f, nil + } + // Try 500Mhz. + div, wait = findDivisorExact(clk500MHz, f, maxWaitCycles) + if div != 0 { + return clockSrcPLLD, div, wait, f, nil + } + + // Try with up to 10x oversampling. This is generally useful for lower + // frequencies, below 10kHz. Prefer the one with less oversampling. Only for + // non-aliased matches. + div19, wait19, f19 := findDivisorOversampled(clk19dot2MHz, f, maxWaitCycles) + div500, wait500, f500 := findDivisorOversampled(clk500MHz, f, maxWaitCycles) + if div19 != 0 && (div500 == 0 || f19 < f500) { + return clockSrc19dot2MHz, div19, wait19, f19, nil + } + if div500 != 0 { + return clockSrcPLLD, div500, wait500, f500, nil + } + return 0, 0, 0, 0, errors.New("failed to find a good clock") +} + +// set changes the clock frequency to the desired value or the closest one +// otherwise. +// +// f=0 means disabled. +// +// maxWaitCycles is the maximum oversampling via an additional wait cycles that +// can further divide the clock. Use 1 if no additional wait cycle is +// available. It is expected to be dmaWaitcyclesMax+1. +// +// Returns the actual clock used and divisor. +func (c *clock) set(f physic.Frequency, maxWaitCycles uint32) (physic.Frequency, uint32, error) { + if f == 0 { + c.ctl = clockPasswdCtl | clockKill + for c.ctl&clockBusy != 0 { + } + return 0, 0, nil + } + ctl, div, div2, actual, err := calcSource(f, maxWaitCycles) + if err != nil { + return 0, 0, err + } + return actual, div2, c.setRaw(ctl, div) +} + +// setRaw sets the clock speed with the clock source and the divisor. +func (c *clock) setRaw(ctl clockCtl, div uint32) error { + if div < 1 || div > clockDiviMax { + return errors.New("invalid clock divisor") + } + if ctl != clockSrc19dot2MHz && ctl != clockSrcPLLD { + return errors.New("invalid clock control") + } + // Stop the clock. + // TODO(maruel): Do not stop the clock if the current clock rate is the one + // desired. + for c.ctl&clockBusy != 0 { + c.ctl = clockPasswdCtl | clockKill + } + d := clockDiv(div << clockDiviShift) + c.div = clockPasswdDiv | d + Nanospin(10 * time.Nanosecond) + // Page 107 + c.ctl = clockPasswdCtl | ctl + Nanospin(10 * time.Nanosecond) + c.ctl = clockPasswdCtl | ctl | clockEnable + if c.div != d { + // This error is mocked out in tests, so the code path of set() callers can + // follow on. + return errClockRegister + } + return nil +} + +func (c *clock) String() string { + return fmt.Sprintf("%s / %s", c.ctl, c.div) +} + +// clockMap is the memory mapped clock registers. +// +// The clock #1 must not be touched since it is being used by the ethernet +// controller. +// +// Page 107 for gp0~gp2. +// https://scribd.com/doc/127599939/BCM2835-Audio-clocks for PCM/PWM. +type clockMap struct { + reserved0 [0x70 / 4]uint32 // + gp0 clock // CM_GP0CTL+CM_GP0DIV; 0x70-0x74 (125MHz max) + gp1ctl uint32 // CM_GP1CTL+CM_GP1DIV; 0x78-0x7A must not use (used by ethernet) + gp1div uint32 // CM_GP1CTL+CM_GP1DIV; 0x78-0x7A must not use (used by ethernet) + gp2 clock // CM_GP2CTL+CM_GP2DIV; 0x80-0x84 (125MHz max) + reserved1 [(0x98 - 0x88) / 4]uint32 // 0x88-0x94 + pcm clock // CM_PCMCTL+CM_PCMDIV 0x98-0x9C + pwm clock // CM_PWMCTL+CM_PWMDIV 0xA0-0xA4 +} + +func (c *clockMap) GoString() string { + return fmt.Sprintf( + "{\n gp0: %s,\n gp1: %s,\n gp2: %s,\n pcm: %sw,\n pwm: %s,\n}", + &c.gp0, &clock{clockCtl(c.gp1ctl), clockDiv(c.gp1div)}, &c.gp2, &c.pcm, &c.pwm) +} diff --git a/vendor/periph.io/x/periph/host/bcm283x/dma.go b/vendor/periph.io/x/periph/host/bcm283x/dma.go new file mode 100644 index 0000000..d6fe719 --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/dma.go @@ -0,0 +1,1224 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// The DMA controller can be used for two functionality: +// - implement zero-CPU continuous PWM. +// - bitbang a large stream of bits over a GPIO pin, for example for WS2812b +// support. +// +// The way it works under the hood is that the bcm283x has two registers, one +// to set a bit and one to clear a bit. +// +// So two DMA controllers are used, one writing a "clear bit" stream and one +// for the "set bit" stream. This requires two independent 32 bits wide streams +// per period for write but only one for read. +// +// References +// +// Page 7: +// " Software accessing RAM directly must use physical addresses (based at +// 0x00000000). Software accessing RAM using the DMA engines must use bus +// addresses (based at 0xC0000000) " ... to skip the L1 cache. +// +// " The BCM2835 DMA Controller provides a total of 16 DMA channels. Each +// channel operates independently from the others and is internally arbitrated +// onto one of the 3 system buses. This means that the amount of bandwidth that +// a DMA channel may consume can be controlled by the arbiter settings. " +// +// The CPU has 16 DMA channels but only the first 7 (#0 to #6) can do strides. +// 7~15 have half the bandwidth. + +// +// References +// +// DMA channel allocation: +// https://github.com/raspberrypi/linux/issues/1327 +// +// DMA location: +// https://www.raspberrypi.org/forums/viewtopic.php?f=71&t=19797 + +package bcm283x + +import ( + "errors" + "fmt" + "log" + "os" + "strings" + "time" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio/gpiostream" + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/host/pmem" + "periph.io/x/periph/host/videocore" +) + +const ( + periphMask = 0x00FFFFFF + periphBus = 0x7E000000 + // maxLite is the maximum transfer allowed by a lite channel. + maxLite = 65535 +) + +// Pages 47-50 +type dmaStatus uint32 + +const ( + dmaReset dmaStatus = 1 << 31 // RESET; Writing a 1 to this bit will reset the DMA + dmaAbort dmaStatus = 1 << 30 // ABORT; Writing a 1 to this bit will abort the current DMA CB. The DMA will load the next CB and attempt to continue. + dmaDisableDebug dmaStatus = 1 << 29 // DISDEBUG; When set to 1, the DMA will not stop when the debug pause signal is asserted. + // When set to 1, the DMA will keep a tally of the AXI writes going out and + // the write responses coming in. At the very end of the current DMA transfer + // it will wait until the last outstanding write response has been received + // before indicating the transfer is complete. Whilst waiting it will load + // the next CB address (but will not fetch the CB), clear the active flag (if + // the next CB address = zero), and it will defer setting the END flag or the + // INT flag until the last outstanding write response has been received. + // In this mode, the DMA will pause if it has more than 13 outstanding writes + // at any one time. + dmaWaitForOutstandingWrites dmaStatus = 1 << 28 // WAIT_FOR_OUTSTANDING_WRITES + // 27:24 reserved + // 23:20 Lowest has higher priority on AXI. + dmaPanicPriorityShift = 20 + dmaPanicPriorityMask = 0xF << 20 // PANIC_PRIORITY + // 19:16 Lowest has higher priority on AXI. + dmaPriorityShift = 16 + dmaPriorityMask = 0xF << dmaPriorityShift // PRIORITY + // 15:9 reserved + dmaErrorStatus dmaStatus = 1 << 8 // ERROR DMA error was detected; must be cleared manually. + // 7 reserved + dmaWaitingForOutstandingWrites dmaStatus = 1 << 6 // WAITING_FOR_OUTSTANDING_WRITES; Indicates if the DMA is currently waiting for any outstanding writes to be received, and is not transferring data. + dmaDreqStopsDMA dmaStatus = 1 << 5 // DREQ_STOPS_DMA; Indicates if the DMA is currently paused and not transferring data due to the DREQ being inactive. + // Indicates if the DMA is currently paused and not transferring data. This + // will occur if: the active bit has been cleared, if the DMA is currently + // executing wait cycles or if the debug_pause signal has been set by the + // debug block, or the number of outstanding writes has exceeded the max + // count. + dmaPaused dmaStatus = 1 << 4 // PAUSED + // Indicates the state of the selected DREQ (Data Request) signal, ie. the + // DREQ selected by the PERMAP field of the transfer info. + // 1 = Requesting data. This will only be valid once the DMA has started and + // the PERMAP field has been loaded from the CB. It will remain valid, + // indicating the selected DREQ signal, until a new CB is loaded. If + // PERMAP is set to zero (unpaced transfer) then this bit will read back + // as 1. + // 0 = No data request. + dmaDreq dmaStatus = 1 << 3 // DREQ + // This is set when the transfer for the CB ends and INTEN is set to 1. Once + // set it must be manually cleared down, even if the next CB has INTEN = 0. + // Write 1 to clear. + dmaInterrupt dmaStatus = 1 << 2 // INT + // Set when the transfer described by the current control block is complete. + // Write 1 to clear. + dmaEnd dmaStatus = 1 << 1 // END + // This bit enables the DMA. The DMA will start if this bit is set and the + // CB_ADDR is non zero. The DMA transfer can be paused and resumed by + // clearing, then setting it again. + // This bit is automatically cleared at the end of the complete DMA transfer, + // ie. after a NEXTCONBK = 0x0000_0000 has been loaded. + dmaActive dmaStatus = 1 << 0 // ACTIVE +) + +var dmaStatusMap = []struct { + v dmaStatus + s string +}{ + {dmaReset, "Reset"}, + {dmaAbort, "Abort"}, + {dmaDisableDebug, "DisableDebug"}, + {dmaWaitForOutstandingWrites, "WaitForOutstandingWrites"}, + {dmaErrorStatus, "ErrorStatus"}, + {dmaWaitingForOutstandingWrites, "WaitingForOutstandingWrites"}, + {dmaDreqStopsDMA, "DreqStopsDMA"}, + {dmaPaused, "Paused"}, + {dmaDreq, "Dreq"}, + {dmaInterrupt, "Interrupt"}, + {dmaEnd, "End"}, + {dmaActive, "Active"}, +} + +func (d dmaStatus) String() string { + var out []string + for _, l := range dmaStatusMap { + if d&l.v != 0 { + d &^= l.v + out = append(out, l.s) + } + } + if v := d & dmaPanicPriorityMask; v != 0 { + out = append(out, fmt.Sprintf("pp%d", v>>dmaPanicPriorityShift)) + d &^= dmaPanicPriorityMask + } + if v := d & dmaPriorityMask; v != 0 { + out = append(out, fmt.Sprintf("p%d", v>>dmaPriorityShift)) + d &^= dmaPriorityMask + } + if d != 0 { + out = append(out, fmt.Sprintf("dmaStatus(0x%x)", uint32(d))) + } + if len(out) == 0 { + return "0" + } + return strings.Join(out, "|") +} + +// Pages 50-52 +type dmaTransferInfo uint32 + +const ( + // 31:27 reserved + // Don't do wide writes as 2 beat burst; only for channels 0 to 6 + dmaNoWideBursts dmaTransferInfo = 1 << 26 // NO_WIDE_BURSTS + // 25:21 Slows down the DMA throughput by setting the number of dummy cycles + // burnt after each DMA read or write is completed. + dmaWaitCyclesShift = 21 + dmaWaitcyclesMax = 0x1F + dmaWaitCyclesMask dmaTransferInfo = dmaWaitcyclesMax << dmaWaitCyclesShift // WAITS + // 20:16 Peripheral mapping (1-31) whose ready signal shall be used to + // control the rate of the transfers. 0 means continuous un-paced transfer. + // + // It is the source used to pace the data reads and writes operations, each + // pace being a DReq (Data Request). + // + // Page 61 + dmaPerMapShift = 16 + dmaPerMapMask dmaTransferInfo = 31 << dmaPerMapShift + dmaFire dmaTransferInfo = 0 << dmaPerMapShift // PERMAP; Continuous trigger + dmaDSI dmaTransferInfo = 1 << dmaPerMapShift // Display Serial Interface (?) + dmaPCMTX dmaTransferInfo = 2 << dmaPerMapShift // + dmaPCMRX dmaTransferInfo = 3 << dmaPerMapShift // + dmaSMI dmaTransferInfo = 4 << dmaPerMapShift // Secondary Memory Interface (?) + dmaPWM dmaTransferInfo = 5 << dmaPerMapShift // + dmaSPITX dmaTransferInfo = 6 << dmaPerMapShift // + dmaSPIRX dmaTransferInfo = 7 << dmaPerMapShift // + dmaBscSPIslaveTX dmaTransferInfo = 8 << dmaPerMapShift // + dmaBscSPIslaveRX dmaTransferInfo = 9 << dmaPerMapShift // + dmaUnused dmaTransferInfo = 10 << dmaPerMapShift // + dmaEMMC dmaTransferInfo = 11 << dmaPerMapShift // + dmaUARTTX dmaTransferInfo = 12 << dmaPerMapShift // + dmaSDHost dmaTransferInfo = 13 << dmaPerMapShift // + dmaUARTRX dmaTransferInfo = 14 << dmaPerMapShift // + dmaDSI2 dmaTransferInfo = 15 << dmaPerMapShift // Same as DSI + dmaSlimBusMCTX dmaTransferInfo = 16 << dmaPerMapShift // + dmaHDMI dmaTransferInfo = 17 << dmaPerMapShift // 216MHz; potentially a (216MHz/(26+1)) 8MHz copy rate but it fails if HDMI is disabled + dmaSlimBusMCRX dmaTransferInfo = 18 << dmaPerMapShift // + dmaSlimBusDC0 dmaTransferInfo = 19 << dmaPerMapShift // + dmaSlimBusDC1 dmaTransferInfo = 20 << dmaPerMapShift // + dmaSlimBusDC2 dmaTransferInfo = 21 << dmaPerMapShift // + dmaSlimBusDC3 dmaTransferInfo = 22 << dmaPerMapShift // + dmaSlimBusDC4 dmaTransferInfo = 23 << dmaPerMapShift // + dmaScalerFIFO0 dmaTransferInfo = 24 << dmaPerMapShift // Also on SMI; SMI can be disabled with smiDisable + dmaScalerFIFO1 dmaTransferInfo = 25 << dmaPerMapShift // + dmaScalerFIFO2 dmaTransferInfo = 26 << dmaPerMapShift // + dmaSlimBusDC5 dmaTransferInfo = 27 << dmaPerMapShift // + dmaSlimBusDC6 dmaTransferInfo = 28 << dmaPerMapShift // + dmaSlimBusDC7 dmaTransferInfo = 29 << dmaPerMapShift // + dmaSlimBusDC8 dmaTransferInfo = 30 << dmaPerMapShift // + dmaSlimBusDC9 dmaTransferInfo = 31 << dmaPerMapShift // + + dmaBurstLengthShift = 12 + dmaBurstLengthMask dmaTransferInfo = 0xF << dmaBurstLengthShift // BURST_LENGTH 15:12 0 means a single transfer. + dmaSrcIgnore dmaTransferInfo = 1 << 11 // SRC_IGNORE Source won't be read, output will be zeros. + dmaSrcDReq dmaTransferInfo = 1 << 10 // SRC_DREQ + dmaSrcWidth128 dmaTransferInfo = 1 << 9 // SRC_WIDTH 128 bits reads if set, 32 bits otherwise. + dmaSrcInc dmaTransferInfo = 1 << 8 // SRC_INC Increment read pointer by 32/128bits at each read if set. + dmaDstIgnore dmaTransferInfo = 1 << 7 // DEST_IGNORE Do not write. + dmaDstDReq dmaTransferInfo = 1 << 6 // DEST_DREQ + dmaDstWidth128 dmaTransferInfo = 1 << 5 // DEST_WIDTH 128 bits writes if set, 32 bits otherwise. + dmaDstInc dmaTransferInfo = 1 << 4 // DEST_INC Increment write pointer by 32/128bits at each read if set. + dmaWaitResp dmaTransferInfo = 1 << 3 // WAIT_RESP DMA waits for AXI write response. + // 2 reserved + // 2D mode interpret of txLen; linear if unset; only for channels 0 to 6. + dmaTransfer2DMode dmaTransferInfo = 1 << 1 // TDMODE + dmaInterruptEnable dmaTransferInfo = 1 << 0 // INTEN Generate an interrupt upon completion. +) + +var dmaTransferInfoMap = []struct { + v dmaTransferInfo + s string +}{ + {dmaNoWideBursts, "NoWideBursts"}, + {dmaSrcIgnore, "SrcIgnore"}, + {dmaSrcDReq, "SrcDReq"}, + {dmaSrcWidth128, "SrcWidth128"}, + {dmaSrcInc, "SrcInc"}, + {dmaDstIgnore, "DstIgnore"}, + {dmaDstDReq, "DstDReq"}, + {dmaDstWidth128, "DstWidth128"}, + {dmaDstInc, "DstInc"}, + {dmaWaitResp, "WaitResp"}, + {dmaTransfer2DMode, "Transfer2DMode"}, + {dmaInterruptEnable, "InterruptEnable"}, +} + +var dmaPerMap = []string{ + "Fire", + "DSI", + "PCMTX", + "PCMRX", + "SMI", + "PWM", + "SPITX", + "SPIRX", + "BscSPISlaveTX", + "BscSPISlaveRX", + "Unused", + "EMMC", + "UARTTX", + "SDHOST", + "UARTRX", + "DSI2", + "SlimBusMCTX", + "HDMI", + "SlimBusMCRX", + "SlimBusDC0", + "SlimBusDC1", + "SlimBusDC2", + "SlimBusDC3", + "SlimBusDC4", + "ScalerFIFO0", + "ScalerFIFO1", + "ScalerFIFO2", + "SlimBusDC5", + "SlimBusDC6", + "SlimBusDC7", + "SlimBusDC8", + "SlimBusDC9", +} + +func (d dmaTransferInfo) String() string { + var out []string + for _, l := range dmaTransferInfoMap { + if d&l.v != 0 { + d &^= l.v + out = append(out, l.s) + } + } + if v := d & dmaWaitCyclesMask; v != 0 { + out = append(out, fmt.Sprintf("waits=%d", v>>dmaWaitCyclesShift)) + d &^= dmaWaitCyclesMask + } + if v := d & dmaBurstLengthMask; v != 0 { + out = append(out, fmt.Sprintf("burst=%d", v>>dmaBurstLengthShift)) + d &^= dmaBurstLengthMask + } + out = append(out, dmaPerMap[(d&dmaPerMapMask)>>dmaPerMapShift]) + d &^= dmaPerMapMask + if d != 0 { + out = append(out, fmt.Sprintf("dmaTransferInfo(0x%x)", uint32(d))) + } + return strings.Join(out, "|") +} + +// Page 55 +type dmaDebug uint32 + +const ( + // 31:29 reserved + dmaLite dmaDebug = 1 << 28 // LITE RO set for lite DMA controllers + // 27:25 version + dmaVersionShift = 25 + dmaVersionMask dmaDebug = 7 << dmaVersionShift // VERSION + // 24:16 dmaState + dmaStateShift = 16 + dmaStateMask dmaDebug = 0x1FF << dmaStateShift // DMA_STATE; the actual states are not documented + // 15:8 dmaID + dmaIDShift = 8 + dmaIDMask = 0xFF << dmaIDShift // DMA_ID; the index of the DMA controller + // 7:4 outstandingWrites + dmaOutstandingWritesShift = 4 + dmaOutstandingWritesMask = 0xF << dmaOutstandingWritesShift // OUTSTANDING_WRITES + // 3 reserved + dmaReadError dmaDebug = 1 << 2 // READ_ERROR slave read error; clear by writing a 1 + dmaFIFOError dmaDebug = 1 << 1 // FIF_ERROR fifo error; clear by writing a 1 + dmaReadLastNotSetError dmaDebug = 1 << 0 // READ_LAST_NOT_SET_ERROR last AXI read signal was not set when expected +) + +var dmaDebugMap = []struct { + v dmaDebug + s string +}{ + {dmaLite, "Lite"}, + {dmaReadError, "ReadError"}, + {dmaFIFOError, "FIFOError"}, + {dmaReadLastNotSetError, "ReadLastNotSetError"}, +} + +func (d dmaDebug) String() string { + var out []string + for _, l := range dmaDebugMap { + if d&l.v != 0 { + d &^= l.v + out = append(out, l.s) + } + } + if v := d & dmaVersionMask; v != 0 { + out = append(out, fmt.Sprintf("v%d", uint32(v>>dmaVersionShift))) + d &^= dmaVersionMask + } + if v := d & dmaStateMask; v != 0 { + out = append(out, fmt.Sprintf("state(%x)", uint32(v>>dmaStateShift))) + d &^= dmaStateMask + } + if v := d & dmaIDMask; v != 0 { + out = append(out, fmt.Sprintf("#%x", uint32(v>>dmaIDShift))) + d &^= dmaIDMask + } + if v := d & dmaOutstandingWritesMask; v != 0 { + out = append(out, fmt.Sprintf("OutstandingWrites=%d", uint32(v>>dmaOutstandingWritesShift))) + d &^= dmaOutstandingWritesMask + } + if d != 0 { + out = append(out, fmt.Sprintf("dmaDebug(0x%x)", uint32(d))) + } + if len(out) == 0 { + return "0" + } + return strings.Join(out, "|") +} + +// 31:30 0 +// 29:16 yLength (only for channels #0 to #6) +// 15:0 xLength +type dmaTransferLen uint32 + +// 31:16 dstStride byte increment to apply at the end of each row in 2D mode +// 15:0 srcStride byte increment to apply at the end of each row in 2D mode +type dmaStride uint32 + +func (d dmaStride) String() string { + y := (d >> 16) & 0xFFFF + if y != 0 { + return fmt.Sprintf("0x%x,0x%x", uint32(y), uint32(d&0xFFFF)) + } + return fmt.Sprintf("0x%x", uint32(d&0xFFFF)) +} + +// controlBlock is 256 bits (32 bytes) in length. +// +// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +// Page 40. +type controlBlock struct { + transferInfo dmaTransferInfo // 0x00 TI + srcAddr uint32 // 0x04 SOURCE_AD pointer to source in physical address space + dstAddr uint32 // 0x08 DEST_AD pointer to destination in physical address space + txLen dmaTransferLen // 0x0C TXFR_LEN length in bytes + stride dmaStride // 0x10 STRIDE + // Pointer to the next chained controlBlock; must be 32 bytes aligned. + // Set it to 0 to stop. + nextCB uint32 // 0x14 NEXTCONBK + reserved [2]uint32 // 0x18+0x1C +} + +// initBlock initializes a controlBlock for any valid DMA operation. +// +// l is in bytes, not in words. +// +// dreq can be dmaFire, dmaPwm, dmaPcmTx, etc. waits is additional wait state +// between clocks. +func (c *controlBlock) initBlock(srcAddr, dstAddr, l uint32, srcIO, dstIO, srcInc, dstInc bool, dreq dmaTransferInfo) error { + if srcIO && dstIO { + return errors.New("only one of src and dst can be I/O") + } + if srcAddr == 0 && dstAddr == 0 { + return errors.New("at least one source or destination is required") + } + if srcAddr == 0 && srcIO { + return errors.New("using src as I/O requires src") + } + if dstAddr == 0 && dstIO { + return errors.New("using dst as I/O requires dst") + } + if dreq&^dmaPerMapMask != 0 { + return errors.New("dreq must be one of the clock source, nothing else") + } + + t := dmaNoWideBursts | dmaWaitResp + if srcAddr == 0 { + t |= dmaSrcIgnore + c.srcAddr = 0 + } else { + if srcIO { + // Memory mapped register + c.srcAddr = physToBus(srcAddr) + } else { + // Normal memory + c.srcAddr = physToUncachedPhys(srcAddr) + } + if srcInc { + t |= dmaSrcInc + } + } + if dstAddr == 0 { + t |= dmaDstIgnore + c.dstAddr = 0 + } else { + if dstIO { + // Memory mapped register + c.dstAddr = physToBus(dstAddr) + } else { + // Normal memory + c.dstAddr = physToUncachedPhys(dstAddr) + } + if dstInc { + t |= dmaDstInc + } + } + if dreq != dmaFire { + // Inserting a wait prevents multiple transfers in a single DReq cycle. + waits := 1 + t |= dreq | dmaTransferInfo(waits<= 0; i-- { + for _, exclude := range blacklist { + if i == exclude { + goto skip + } + } + if drvDMA.dmaMemory.channels[i].isAvailable() { + drvDMA.dmaMemory.channels[i].reset() + return i, &drvDMA.dmaMemory.channels[i] + } + skip: + } + } + // Uncomment to understand the state of the DMA channels. + //log.Printf("%#v", drvDMA.dmaMemory) + return -1, nil +} + +// runIO picks a DMA channel, initialize it and runs a transfer. +// +// It tries to release the channel as soon as it can. +func runIO(pCB pmem.Mem, liteOk bool) error { + var blacklist []int + if !liteOk { + blacklist = []int{7, 8, 9, 10, 11, 12, 13, 14, 15} + } + _, ch := pickChannel(blacklist...) + if ch == nil { + return errors.New("bcm283x-dma: no channel available") + } + defer ch.reset() + ch.startIO(uint32(pCB.PhysAddr())) + return ch.wait() +} + +func allocateCB(size int) ([]controlBlock, *videocore.Mem, error) { + buf, err := drvDMA.dmaBufAllocator((size + 0xFFF) &^ 0xFFF) + if err != nil { + return nil, nil, err + } + var cb []controlBlock + if err := buf.AsPOD(&cb); err != nil { + _ = buf.Close() + return nil, nil, err + } + return cb, buf, nil +} + +// dmaWriteStreamPCM streams data to a PCM enabled pin as a half-duplex I²S +// channel. +func dmaWriteStreamPCM(p *Pin, w gpiostream.Stream) error { + d := w.Duration() + if d == 0 { + return nil + } + f := w.Frequency() + _, _, _, actualfreq, err := calcSource(f, 1) + if err != nil { + return err + } + if actualfreq != f { + return errors.New("TODO(maruel): handle oversampling") + } + + // Start clock earlier. + drvDMA.pcmMemory.reset() + _, _, err = setPCMClockSource(f) + if err != nil { + return err + } + + // Calculate the number of bytes needed. + l := (int(w.Frequency()/f) + 7) / 8 // Bytes + buf, err := drvDMA.dmaBufAllocator((l + 0xFFF) &^ 0xFFF) + if err != nil { + return err + } + defer buf.Close() + if err := copyStreamToDMABuf(w, buf.Uint32()); err != nil { + return err + } + + cb, pCB, err := allocateCB(4096) + if err != nil { + return err + } + defer pCB.Close() + reg := drvDMA.pcmBaseAddr + 0x4 // pcmMap.fifo + if err = cb[0].initBlock(uint32(buf.PhysAddr()), reg, uint32(l), false, true, true, false, dmaPCMTX); err != nil { + return err + } + + defer drvDMA.pcmMemory.reset() + // Start transfer + drvDMA.pcmMemory.set() + err = runIO(pCB, l <= maxLite) + // We have to wait PCM to be finished even after DMA finished. + for drvDMA.pcmMemory.cs&pcmTXErr == 0 { + Nanospin(10 * time.Nanosecond) + } + return err +} + +func dmaWritePWMFIFO() (*dmaChannel, *videocore.Mem, error) { + if drvDMA.dmaMemory == nil { + return nil, nil, errors.New("bcm283x-dma is not initialized; try running as root?") + } + cb, buf, err := allocateCB(32 + 4) // CB + data + if err != nil { + return nil, nil, err + } + u := buf.Uint32() + offsetBytes := uint32(32) + u[offsetBytes/4] = 0x0 + physBuf := uint32(buf.PhysAddr()) + physBit := physBuf + offsetBytes + dest := drvDMA.pwmBaseAddr + 0x18 // PWM FIFO + if err := cb[0].initBlock(physBit, dest, 4, false, true, false, false, dmaPWM); err != nil { + _ = buf.Close() + return nil, nil, err + } + cb[0].nextCB = physBuf // Loop back to self. + + _, ch := pickChannel() + if ch == nil { + _ = buf.Close() + return nil, nil, errors.New("bcm283x-dma: no channel available") + } + ch.startIO(physBuf) + + return ch, buf, nil +} + +func startPWMbyDMA(p *Pin, rng, data uint32) (*dmaChannel, *videocore.Mem, error) { + if drvDMA.dmaMemory == nil { + return nil, nil, errors.New("bcm283x-dma is not initialized; try running as root?") + } + cb, buf, err := allocateCB(2*32 + 4) // 2 CBs + mask + if err != nil { + return nil, nil, err + } + u := buf.Uint32() + cbBytes := uint32(32) + offsetBytes := cbBytes * 2 + u[offsetBytes/4] = uint32(1) << uint(p.number&31) + physBuf := uint32(buf.PhysAddr()) + physBit := physBuf + offsetBytes + dest := [2]uint32{ + drvGPIO.gpioBaseAddr + 0x28 + 4*uint32(p.number/32), // clear + drvGPIO.gpioBaseAddr + 0x1C + 4*uint32(p.number/32), // set + } + // High + if err := cb[0].initBlock(physBit, dest[1], data*4, false, true, false, false, dmaPWM); err != nil { + _ = buf.Close() + return nil, nil, err + } + cb[0].nextCB = physBuf + cbBytes + // Low + if err := cb[1].initBlock(physBit, dest[0], (rng-data)*4, false, true, false, false, dmaPWM); err != nil { + _ = buf.Close() + return nil, nil, err + } + cb[1].nextCB = physBuf // Loop back to cb[0] + + var blacklist []int + if data*4 >= 1<<16 || (rng-data)*4 >= 1<<16 { + // Don't use lite channels. + blacklist = []int{7, 8, 9, 10, 11, 12, 13, 14, 15} + } + _, ch := pickChannel(blacklist...) + + if ch == nil { + _ = buf.Close() + return nil, nil, errors.New("bcm283x-dma: no channel available") + } + ch.startIO(physBuf) + + return ch, buf, nil +} + +// overSamples calculates the skip value which are the values that are read but +// discarded as the clock is too fast. +func overSamples(s gpiostream.Stream) (int, error) { + desired := s.Frequency() + skip := drvDMA.pwmDMAFreq / desired + if skip < 1 { + return 0, fmt.Errorf("frequency is too high(%s)", desired) + } + actualFreq := drvDMA.pwmDMAFreq / skip + errorPercent := 100 * (actualFreq - desired) / desired + if errorPercent < -10 || errorPercent > 10 { + return 0, fmt.Errorf("actual resolution differs more than 10%%(%s vs %s)", desired, actualFreq) + } + return int(skip), nil +} + +// dmaReadStream streams input from a pin. +func dmaReadStream(p *Pin, b *gpiostream.BitStream) error { + skip, err := overSamples(b) + if err != nil { + return err + } + if _, err := setPWMClockSource(); err != nil { + return err + } + + // Needs 32x the memory since each read is one full uint32. On the other + // hand one could read 32 contiguous pins simultaneously at no cost. + // TODO(simokawa): Implement a function to get number of bits for all type of + // Stream + l := len(b.Bits) * 8 * 4 * int(skip) + // TODO(simokawa): Allocate multiple pages and CBs for huge buffer. + buf, err := drvDMA.dmaBufAllocator((l + 0xFFF) &^ 0xFFF) + if err != nil { + return err + } + defer buf.Close() + cb, pCB, err := allocateCB(4) + if err != nil { + return err + } + defer pCB.Close() + + reg := drvGPIO.gpioBaseAddr + 0x34 + 4*uint32(p.number/32) // GPIO Pin Level 0 + if err := cb[0].initBlock(reg, uint32(buf.PhysAddr()), uint32(l), true, false, false, true, dmaPWM); err != nil { + return err + } + err = runIO(pCB, l <= maxLite) + uint32ToBitLSBF(b.Bits, buf.Bytes(), uint8(p.number&31), skip*4) + return err +} + +// dmaWriteStreamEdges streams data to a pin as a half-duplex one controlBlock +// per bit toggle DMA stream. +// +// Memory usage is 32 bytes x number of bit changes rounded up to nearest +// 4Kb, so an arbitrary stream of 1s or 0s only takes 4Kb but a stream of +// 101010s will takes 256x the memory. +// +// TODO(maruel): Use huffman-coding-like repeated patterns detection to +// "compress" the bitstream. This trades off upfront computation for lower +// memory usage. The "compressing" function should be public, so the user can +// call it only once yet stream multiple times. +// +// TODO(maruel): Mutate the program as it goes to reduce duplication by having +// the DMA controller write in a following controlBlock.nextCB. +// handling gpiostream.Program explicitly. +func dmaWriteStreamEdges(p *Pin, w gpiostream.Stream) error { + d := w.Duration() + if d == 0 { + return nil + } + var bits []byte + var msb bool + switch v := w.(type) { + case *gpiostream.BitStream: + bits = v.Bits + msb = !v.LSBF + default: + return fmt.Errorf("Unknown type: %T", v) + } + skip, err := overSamples(w) + if err != nil { + return err + } + + // Calculate the number of controlBlock needed. + count := 1 + stride := uint32(skip) + last := getBit(bits[0], 0, msb) + l := int(int64(d) * int64(w.Frequency()) / int64(physic.Hertz)) // Bits + for i := 1; i < l; i++ { + if v := getBit(bits[i/8], i%8, msb); v != last || stride == maxLite { + last = v + count++ + stride = 0 + } + stride += uint32(skip) + } + // 32 bytes for each CB and 4 bytes for the mask. + bufBytes := count*32 + 4 + cb, buf, err := allocateCB((bufBytes + 0xFFF) &^ 0xFFF) + if err != nil { + return err + } + defer buf.Close() + + // Setup the single mask buffer of 4Kb. + mask := uint32(1) << uint(p.number&31) + u := buf.Uint32() + offset := (len(buf.Bytes()) - 4) + u[offset/4] = mask + physBit := uint32(buf.PhysAddr()) + uint32(offset) + + // Other constants during the loop. + // Waits does not seem to work as expected. Not counted as DREQ pulses? + // Use PWM's rng1 instead for this. + //waits := divs - 1 + dest := [2]uint32{ + drvGPIO.gpioBaseAddr + 0x28 + 4*uint32(p.number/32), // clear + drvGPIO.gpioBaseAddr + 0x1C + 4*uint32(p.number/32), // set + } + + // Render the controlBlock's to trigger the bit trigger for either Set or + // Clear GPIO memory registers. + last = getBit(bits[0], 0, msb) + index := 0 + stride = uint32(skip) + for i := 1; i < l; i++ { + if v := getBit(bits[i/8], i%8, msb); v != last || stride == maxLite { + if err := cb[index].initBlock(physBit, dest[last], stride*4, false, true, false, false, dmaPWM); err != nil { + return err + } + // Hardcoded len(controlBlock) == 32. It is not necessary to use + // physToUncachedPhys() here. + cb[index].nextCB = uint32(buf.PhysAddr()) + uint32(32*(index+1)) + index++ + stride = 0 + last = v + } + stride += uint32(skip) + } + if err := cb[index].initBlock(physBit, dest[last], stride*4, false, true, false, false, dmaPWM); err != nil { + return err + } + + // Start clock before DMA + _, err = setPWMClockSource() + if err != nil { + return err + } + return runIO(buf, true) +} + +// dmaWriteStreamDualChannel streams data to a pin using two DMA channels. +// +// In practice this leads to a glitchy stream. +func dmaWriteStreamDualChannel(p *Pin, w gpiostream.Stream) error { + // TODO(maruel): Analyse 'w' to figure out the programs to load, and create + // the number of controlBlock needed to reduce memory usage. + // TODO(maruel): When only one channel is needed, it is much more memory + // efficient to use DMA to write to PWM FIFO. + skip, err := overSamples(w) + if err != nil { + return err + } + // Calculates the number of needed bytes. + l := int(int64(w.Duration())*int64(w.Frequency())/int64(physic.Hertz)) * skip * 4 + bufLen := (l + 0xFFF) &^ 0xFFF + bufSet, err := drvDMA.dmaBufAllocator(bufLen) + if err != nil { + return err + } + defer bufSet.Close() + bufClear, err := drvDMA.dmaBufAllocator(bufLen) + if err != nil { + return err + } + defer bufClear.Close() + cb, pCB, err := allocateCB(4096) + if err != nil { + return err + } + defer pCB.Close() + + // Needs 64x the memory since each write is 2 full uint32. On the other + // hand one could write 32 contiguous pins simultaneously at no cost. + mask := uint32(1) << uint(p.number&31) + if err := raster32(w, skip, bufClear.Uint32(), bufSet.Uint32(), mask); err != nil { + return err + } + + // Start clock before DMA start + _, err = setPWMClockSource() + if err != nil { + return err + } + + regSet := drvGPIO.gpioBaseAddr + 0x1C + 4*uint32(p.number/32) + if err := cb[0].initBlock(uint32(bufSet.PhysAddr()), regSet, uint32(l), false, true, true, false, dmaPWM); err != nil { + return err + } + regClear := drvGPIO.gpioBaseAddr + 0x28 + 4*uint32(p.number/32) + if err := cb[1].initBlock(uint32(bufClear.PhysAddr()), regClear, uint32(l), false, true, true, false, dmaPWM); err != nil { + return err + } + + // The first channel must be a full bandwidth one. The "light" ones are + // effectively a single one, which means that they are interleaved. If both + // are "light" then the jitter is largely increased. + x, chSet := pickChannel(6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + if chSet == nil { + return errors.New("bcm283x-dma: no channel available") + } + defer chSet.reset() + _, chClear := pickChannel(x) + if chClear == nil { + return errors.New("bcm283x-dma: no secondary channel available") + } + defer chClear.reset() + + // Two channel need to be synchronized but there is not such a mechanism. + chSet.startIO(uint32(pCB.PhysAddr())) // cb[0] + chClear.startIO(uint32(pCB.PhysAddr()) + 32) // cb[1] + + err1 := chSet.wait() + err2 := chClear.wait() + if err1 == nil { + return err2 + } + return err1 +} + +// physToUncachedPhys returns the uncached physical memory address backing a +// physical memory address. +// +// p must be rooted at a page boundary (4096). +func physToUncachedPhys(p uint32) uint32 { + // http://en.wikibooks.org/wiki/Aros/Platforms/Arm_Raspberry_Pi_support#Framebuffer + return p | drvGPIO.dramBus +} + +func physToBus(p uint32) uint32 { + return (p & periphMask) | periphBus +} + +// smokeTest allocates two physical pages, ask the DMA controller to copy the +// data from one page to another and make sure the content is as expected. +// +// This should take a fraction of a second and will make sure the driver is +// usable. This ensures there's at least one DMA channel available. +func smokeTest() error { + // If these are commented out due to a new processor having different + // characteristics, the corresponding code needs to be updated. + if drvDMA.dmaMemory.channels[6].debug&dmaLite != 0 { + return errors.New("unexpected hardware: DMA channel #6 shouldn't be lite") + } + if drvDMA.dmaMemory.channels[7].debug&dmaLite == 0 { + return errors.New("unexpected hardware: DMA channel #7 should be lite") + } + if drvDMA.dmaMemory.enable != 0x7FFF { + return errors.New("unexpected hardware: DMA enable is not fully set") + } + + const size = 4096 * 4 // 16kb + const holeSize = 1 // Minimum DMA alignment + + alloc := func(s int) (pmem.Mem, error) { + return videocore.Alloc(s) + } + + copyMem := func(pDst, pSrc uint64) error { + // Allocate a control block and initialize it. + pCB, err2 := videocore.Alloc(4096) + if err2 != nil { + return err2 + } + defer pCB.Close() + var cb *controlBlock + if err := pCB.AsPOD(&cb); err != nil { + return err + } + if false { + // This code is not run by default because it resets the PWM clock on + // process startup, which may cause undesirable glitches. + + // Initializes the PWM clock right away to 1MHz. + _, err := setPWMClockSource() + if err != nil { + return err + } + if err := cb.initBlock(uint32(pSrc), uint32(pDst)+holeSize, size-2*holeSize, false, false, true, true, dmaPWM); err != nil { + return err + } + } else { + // Use maximum performance. + if err := cb.initBlock(uint32(pSrc), uint32(pDst)+holeSize, size-2*holeSize, false, false, true, true, dmaFire); err != nil { + return err + } + } + return runIO(pCB, size-2*holeSize <= maxLite) + } + + return pmem.TestCopy(size, holeSize, alloc, copyMem) +} + +// driverDMA implements periph.Driver. +// +// It implements much more than the DMA controller, it also exposes the clocks, +// the PWM and PCM controllers. +type driverDMA struct { + pcmBaseAddr uint32 + pwmBaseAddr uint32 + + dmaMemory *dmaMap + dmaChannel15 *dmaChannel + pcmMemory *pcmMap + clockMemory *clockMap + timerMemory *timerMap + gpioPadMemory *gpioPadMap + // Page 138 + // - Two independent bit-streams + // - Each channel either a PWM or serialised version of a 32-bit word + // - Variable input and output resolutions. + // - Load data from a FIFO storage block, to extent to 8 32-bit words (256 + // bits). + // + // Author note: 100Mhz base resolution with a 256 bits 1-bit stream is actually + // good enough to generate a DAC. + pwmMemory *pwmMap + + // These clocks are shared with hardware PWM, DMA driven PWM and BitStream. + pwmBaseFreq physic.Frequency + pwmDMAFreq physic.Frequency + pwmDMACh *dmaChannel + pwmDMABuf *videocore.Mem + + // dmaBufAllocator is overriden for unit testing. + dmaBufAllocator func(s int) (*videocore.Mem, error) // Set to videocore.Alloc +} + +func (d *driverDMA) Close() error { + // TODO(maruel): Stop DMA and PWM controllers. + d.pcmBaseAddr = 0 + d.pwmBaseAddr = 0 + d.dmaMemory = nil + d.dmaChannel15 = nil + d.pcmMemory = nil + d.clockMemory = nil + d.timerMemory = nil + d.pwmMemory = nil + d.pwmBaseFreq = 0 + d.pwmDMAFreq = 0 + d.pwmDMACh = nil + d.pwmDMABuf = nil + d.dmaBufAllocator = nil + return nil +} + +func (d *driverDMA) String() string { + return "bcm283x-dma" +} + +func (d *driverDMA) Prerequisites() []string { + return []string{"bcm283x-gpio"} +} + +func (d *driverDMA) After() []string { + return nil +} + +func (d *driverDMA) Init() (bool, error) { + d.dmaBufAllocator = videocore.Alloc + d.pwmBaseFreq = 25 * physic.MegaHertz + d.pwmDMAFreq = 200 * physic.KiloHertz + // baseAddr is initialized by prerequisite driver bcm283x-gpio. + if err := pmem.MapAsPOD(uint64(drvGPIO.baseAddr+0x7000), &d.dmaMemory); err != nil { + if os.IsPermission(err) { + return true, fmt.Errorf("need more access, try as root: %v", err) + } + return true, err + } + // Channel #15 is "physically removed from the other DMA Channels so it has a + // different address base". + if err := pmem.MapAsPOD(uint64(drvGPIO.baseAddr+0xE05000), &d.dmaChannel15); err != nil { + return true, err + } + d.pcmBaseAddr = drvGPIO.baseAddr + 0x203000 + if err := pmem.MapAsPOD(uint64(d.pcmBaseAddr), &d.pcmMemory); err != nil { + return true, err + } + d.pwmBaseAddr = drvGPIO.baseAddr + 0x20C000 + if err := pmem.MapAsPOD(uint64(d.pwmBaseAddr), &d.pwmMemory); err != nil { + return true, err + } + if err := pmem.MapAsPOD(uint64(drvGPIO.baseAddr+0x101000), &d.clockMemory); err != nil { + return true, err + } + if err := pmem.MapAsPOD(uint64(drvGPIO.baseAddr+0x3000), &d.timerMemory); err != nil { + return true, err + } + if err := pmem.MapAsPOD(uint64(drvGPIO.baseAddr+0x100000), &d.gpioPadMemory); err != nil { + return true, err + } + // Do not run smokeTest() unless it's clear it is not dangerous. + return true, nil +} + +func debugDMA() { + for i, ch := range drvDMA.dmaMemory.channels { + log.Println(i, ch.cs.String()) + if ch.cs&dmaActive != 0 { + log.Printf("%x: %s", ch.cbAddr, ch.GoString()) + } + } + log.Println(15, drvDMA.dmaChannel15.cs.String()) +} + +func resetDMA(ch int) error { + if ch < len(drvDMA.dmaMemory.channels) { + drvDMA.dmaMemory.channels[ch].reset() + } else if ch == 15 { + drvDMA.dmaChannel15.reset() + } else { + return fmt.Errorf("invalid dma channel %d", ch) + } + return nil +} + +func init() { + if isArm { + periph.MustRegister(&drvDMA) + } +} + +var drvDMA driverDMA diff --git a/vendor/periph.io/x/periph/host/bcm283x/doc.go b/vendor/periph.io/x/periph/host/bcm283x/doc.go new file mode 100644 index 0000000..d18ea29 --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/doc.go @@ -0,0 +1,42 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package bcm283x exposes the BCM283x GPIO functionality. +// +// This driver implements memory-mapped GPIO pin manipulation and leverages +// sysfs-gpio for edge detection. +// +// If you are looking for the actual implementation, open doc.go for further +// implementation details. +// +// GPIOs +// +// Aliases for GPCLK0, GPCLK1, GPCLK2 are created for corresponding CLKn pins. +// Same for PWM0_OUT and PWM1_OUT, which point respectively to PWM0 and PWM1. +// +// Datasheet +// +// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +// +// Its crowd-sourced errata: http://elinux.org/BCM2835_datasheet_errata +// +// BCM2836: +// https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf +// +// Another doc about PCM and PWM: +// https://scribd.com/doc/127599939/BCM2835-Audio-clocks +// +// GPIO pad control: +// https://scribd.com/doc/101830961/GPIO-Pads-Control2 +package bcm283x + +// Other implementations details +// +// mainline: +// https://github.com/torvalds/linux/blob/master/drivers/dma/bcm2835-dma.c +// https://github.com/torvalds/linux/blob/master/drivers/gpio +// +// Raspbian kernel: +// https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/dma +// https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/gpio diff --git a/vendor/periph.io/x/periph/host/bcm283x/gpio.go b/vendor/periph.io/x/periph/host/bcm283x/gpio.go new file mode 100644 index 0000000..509328d --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/gpio.go @@ -0,0 +1,1361 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package bcm283x + +import ( + "errors" + "fmt" + "os" + "strconv" + "strings" + "time" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/gpio/gpiostream" + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/distro" + "periph.io/x/periph/host/pmem" + "periph.io/x/periph/host/sysfs" + "periph.io/x/periph/host/videocore" +) + +// All the pins supported by the CPU. +var ( + GPIO0 *Pin // I2C0_SDA + GPIO1 *Pin // I2C0_SCL + GPIO2 *Pin // I2C1_SDA + GPIO3 *Pin // I2C1_SCL + GPIO4 *Pin // CLK0 + GPIO5 *Pin // CLK1 + GPIO6 *Pin // CLK2 + GPIO7 *Pin // SPI0_CS1 + GPIO8 *Pin // SPI0_CS0 + GPIO9 *Pin // SPI0_MISO + GPIO10 *Pin // SPI0_MOSI + GPIO11 *Pin // SPI0_CLK + GPIO12 *Pin // PWM0 + GPIO13 *Pin // PWM1 + GPIO14 *Pin // UART0_TX, UART1_TX + GPIO15 *Pin // UART0_RX, UART1_RX + GPIO16 *Pin // UART0_CTS, SPI1_CS2, UART1_CTS + GPIO17 *Pin // UART0_RTS, SPI1_CS1, UART1_RTS + GPIO18 *Pin // I2S_SCK, SPI1_CS0, PWM0 + GPIO19 *Pin // I2S_WS, SPI1_MISO, PWM1 + GPIO20 *Pin // I2S_DIN, SPI1_MOSI, CLK0 + GPIO21 *Pin // I2S_DOUT, SPI1_CLK, CLK1 + GPIO22 *Pin // + GPIO23 *Pin // + GPIO24 *Pin // + GPIO25 *Pin // + GPIO26 *Pin // + GPIO27 *Pin // + GPIO28 *Pin // I2C0_SDA, I2S_SCK + GPIO29 *Pin // I2C0_SCL, I2S_WS + GPIO30 *Pin // I2S_DIN, UART0_CTS, UART1_CTS + GPIO31 *Pin // I2S_DOUT, UART0_RTS, UART1_RTS + GPIO32 *Pin // CLK0, UART0_TX, UART1_TX + GPIO33 *Pin // UART0_RX, UART1_RX + GPIO34 *Pin // CLK0 + GPIO35 *Pin // SPI0_CS1 + GPIO36 *Pin // SPI0_CS0, UART0_TX + GPIO37 *Pin // SPI0_MISO, UART0_RX + GPIO38 *Pin // SPI0_MOSI, UART0_RTS + GPIO39 *Pin // SPI0_CLK, UART0_CTS + GPIO40 *Pin // PWM0, SPI2_MISO, UART1_TX + GPIO41 *Pin // PWM1, SPI2_MOSI, UART1_RX + GPIO42 *Pin // CLK1, SPI2_CLK, UART1_RTS + GPIO43 *Pin // CLK2, SPI2_CS0, UART1_CTS + GPIO44 *Pin // CLK1, I2C0_SDA, I2C1_SDA, SPI2_CS1 + GPIO45 *Pin // PWM1, I2C0_SCL, I2C1_SCL, SPI2_CS2 + GPIO46 *Pin // + // Pins 47~53 are not exposed because using them would lead to immediate SD + // Card corruption. +) + +// Present returns true if running on a Broadcom bcm283x based CPU. +func Present() bool { + if isArm { + hardware, ok := distro.CPUInfo()["Hardware"] + return ok && strings.HasPrefix(hardware, "BCM") + } + return false +} + +// PinsRead0To31 returns the value of all GPIO0 to GPIO31 at their corresponding +// bit as a single read operation. +// +// This function is extremely fast and does no error checking. +// +// The returned bits are valid for both inputs and outputs. +func PinsRead0To31() uint32 { + return drvGPIO.gpioMemory.level[0] +} + +// PinsClear0To31 clears the value of GPIO0 to GPIO31 pin for the bit set at +// their corresponding bit as a single write operation. +// +// This function is extremely fast and does no error checking. +func PinsClear0To31(mask uint32) { + drvGPIO.gpioMemory.outputClear[0] = mask +} + +// PinsSet0To31 sets the value of GPIO0 to GPIO31 pin for the bit set at their +// corresponding bit as a single write operation. +func PinsSet0To31(mask uint32) { + drvGPIO.gpioMemory.outputSet[0] = mask +} + +// PinsRead32To46 returns the value of all GPIO32 to GPIO46 at their +// corresponding 'bit minus 32' as a single read operation. +// +// This function is extremely fast and does no error checking. +// +// The returned bits are valid for both inputs and outputs. +// +// Bits above 15 are guaranteed to be 0. +// +// This function is not recommended on Raspberry Pis as these GPIOs are not +// easily accessible. +func PinsRead32To46() uint32 { + return drvGPIO.gpioMemory.level[1] & 0x7fff +} + +// PinsClear32To46 clears the value of GPIO31 to GPIO46 pin for the bit set at +// their corresponding 'bit minus 32' as a single write operation. +// +// This function is extremely fast and does no error checking. +// +// Bits above 15 are ignored. +// +// This function is not recommended on Raspberry Pis as these GPIOs are not +// easily accessible. +func PinsClear32To46(mask uint32) { + drvGPIO.gpioMemory.outputClear[1] = (mask & 0x7fff) +} + +// PinsSet32To46 sets the value of GPIO31 to GPIO46 pin for the bit set at +// their corresponding 'bit minus 32' as a single write operation. +// +// This function is extremely fast and does no error checking. +// +// Bits above 15 are ignored. +// +// This function is not recommended on Raspberry Pis as these GPIOs are not +// easily accessible. +func PinsSet32To46(mask uint32) { + drvGPIO.gpioMemory.outputSet[1] = (mask & 0x7fff) +} + +// PinsSetup0To27 sets the output current drive strength, output slew limiting +// and input hysteresis for GPIO 0 to 27. +// +// Default drive is 8mA, slew unlimited and hysteresis enabled. +// +// Can only be used if driver bcm283x-dma was loaded. +func PinsSetup0To27(drive physic.ElectricCurrent, slewLimit, hysteresis bool) error { + if drvDMA.gpioPadMemory == nil { + return errors.New("bcm283x-dma not initialized; try again as root?") + } + drvDMA.gpioPadMemory.pads0.set(toPad(drive, slewLimit, hysteresis)) + return nil +} + +// PinsSetup28To45 sets the output current drive strength, output slew limiting +// and input hysteresis for GPIO 28 to 45. +// +// Default drive is 16mA, slew unlimited and hysteresis enabled. +// +// Can only be used if driver bcm283x-dma was loaded. +// +// This function is not recommended on Raspberry Pis as these GPIOs are not +// easily accessible. +func PinsSetup28To45(drive physic.ElectricCurrent, slewLimit, hysteresis bool) error { + if drvDMA.gpioPadMemory == nil { + return errors.New("bcm283x-dma not initialized; try again as root?") + } + drvDMA.gpioPadMemory.pads1.set(toPad(drive, slewLimit, hysteresis)) + return nil +} + +// Pin is a GPIO number (GPIOnn) on BCM238(5|6|7). +// +// Pin implements gpio.PinIO. +type Pin struct { + // Immutable. + number int + name string + defaultPull gpio.Pull // Default pull at system boot, as per datasheet. + + // Immutable after driver initialization. + sysfsPin *sysfs.Pin // Set to the corresponding sysfs.Pin, if any. + + // Mutable. + usingEdge bool // Set when edge detection is enabled. + usingClock bool // Set when a CLK, PWM or I2S/PCM clock is used. + dmaCh *dmaChannel // Set when DMA is used for PWM or I2S/PCM. + dmaBuf *videocore.Mem // Set when DMA is used for PWM or I2S/PCM. +} + +// String implements conn.Resource. +func (p *Pin) String() string { + return p.name +} + +// Halt implements conn.Resource. +// +// If the pin is running a clock, PWM or waiting for edges, it is halted. +// +// In the case of clock or PWM, all pins with this clock source are also +// disabled. +func (p *Pin) Halt() error { + if p.usingEdge { + if err := p.sysfsPin.Halt(); err != nil { + return p.wrap(err) + } + p.usingEdge = false + } + return p.haltClock() +} + +// Name implements pin.Pin. +func (p *Pin) Name() string { + return p.name +} + +// Number implements pin.Pin. +// +// This is the GPIO number, not the pin number on a header. +func (p *Pin) Number() int { + return p.number +} + +// Function implements pin.Pin. +func (p *Pin) Function() string { + return string(p.Func()) +} + +// Func implements pin.PinFunc. +func (p *Pin) Func() pin.Func { + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return pin.Func("ERR") + } + return p.sysfsPin.Func() + } + switch f := p.function(); f { + case in: + if p.FastRead() { + return gpio.IN_HIGH + } + return gpio.IN_LOW + case out: + if p.FastRead() { + return gpio.OUT_HIGH + } + return gpio.OUT_LOW + case alt0: + if s := mapping[p.number][0]; len(s) != 0 { + return s + } + return pin.Func("ALT0") + case alt1: + if s := mapping[p.number][1]; len(s) != 0 { + return s + } + return pin.Func("ALT1") + case alt2: + if s := mapping[p.number][2]; len(s) != 0 { + return s + } + return pin.Func("ALT2") + case alt3: + if s := mapping[p.number][3]; len(s) != 0 { + return s + } + return pin.Func("ALT3") + case alt4: + if s := mapping[p.number][4]; len(s) != 0 { + return s + } + return pin.Func("ALT4") + case alt5: + if s := mapping[p.number][5]; len(s) != 0 { + return s + } + return pin.Func("ALT5") + default: + return pin.Func("ERR") + } +} + +// SupportedFuncs implements pin.PinFunc. +func (p *Pin) SupportedFuncs() []pin.Func { + f := make([]pin.Func, 0, 2+4) + f = append(f, gpio.IN, gpio.OUT) + for _, m := range mapping[p.number] { + if m != "" { + f = append(f, m) + } + } + return f +} + +// SetFunc implements pin.PinFunc. +func (p *Pin) SetFunc(f pin.Func) error { + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible")) + } + return p.sysfsPin.SetFunc(f) + } + switch f { + case gpio.FLOAT: + return p.In(gpio.Float, gpio.NoEdge) + case gpio.IN: + return p.In(gpio.PullNoChange, gpio.NoEdge) + case gpio.IN_LOW: + return p.In(gpio.PullDown, gpio.NoEdge) + case gpio.IN_HIGH: + return p.In(gpio.PullUp, gpio.NoEdge) + case gpio.OUT_HIGH: + return p.Out(gpio.High) + case gpio.OUT_LOW: + return p.Out(gpio.Low) + default: + isGeneral := f == f.Generalize() + for i, m := range mapping[p.number] { + if m == f || (isGeneral && m.Generalize() == f) { + if err := p.Halt(); err != nil { + return err + } + switch i { + case 0: + p.setFunction(alt0) + case 1: + p.setFunction(alt1) + case 2: + p.setFunction(alt2) + case 3: + p.setFunction(alt3) + case 4: + p.setFunction(alt4) + case 5: + p.setFunction(alt5) + } + return nil + } + } + return p.wrap(errors.New("unsupported function")) + } +} + +// In implements gpio.PinIn. +// +// Specifying a value for pull other than gpio.PullNoChange causes this +// function to be slightly slower (about 1ms). +// +// For pull down, the resistor is 50KOhm~60kOhm +// For pull up, the resistor is 50kOhm~65kOhm +// +// The pull resistor stays set even after the processor shuts down. It is not +// possible to 'read back' what value was specified for each pin. +// +// Will fail if requesting to change a pin that is set to special functionality. +// +// Using edge detection requires opening a gpio sysfs file handle. On Raspbian, +// make sure the user is member of group 'gpio'. The pin will be exported at +// /sys/class/gpio/gpio*/. Note that the pin will not be unexported at +// shutdown. +// +// For edge detection, the processor samples the input at its CPU clock rate +// and looks for '011' to rising and '100' for falling detection to avoid +// glitches. Because gpio sysfs is used, the latency is unpredictable. +func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error { + if p.usingEdge && edge == gpio.NoEdge { + if err := p.sysfsPin.Halt(); err != nil { + return p.wrap(err) + } + } + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible")) + } + if pull != gpio.PullNoChange { + return p.wrap(errors.New("pull cannot be used when subsystem gpiomem not initialized")) + } + if err := p.sysfsPin.In(pull, edge); err != nil { + return p.wrap(err) + } + p.usingEdge = edge != gpio.NoEdge + return nil + } + if err := p.haltClock(); err != nil { + return err + } + p.setFunction(in) + if pull != gpio.PullNoChange { + // Changing pull resistor requires a specific dance as described at + // https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf + // page 101. + + // Set Pull + switch pull { + case gpio.PullDown: + drvGPIO.gpioMemory.pullEnable = 1 + case gpio.PullUp: + drvGPIO.gpioMemory.pullEnable = 2 + case gpio.Float: + drvGPIO.gpioMemory.pullEnable = 0 + } + + // Datasheet states caller needs to sleep 150 cycles. + sleep150cycles() + offset := p.number / 32 + drvGPIO.gpioMemory.pullEnableClock[offset] = 1 << uint(p.number%32) + + sleep150cycles() + drvGPIO.gpioMemory.pullEnable = 0 + drvGPIO.gpioMemory.pullEnableClock[offset] = 0 + } + if edge != gpio.NoEdge { + if p.sysfsPin == nil { + return p.wrap(fmt.Errorf("pin %d is not exported by sysfs", p.number)) + } + // This resets pending edges. + if err := p.sysfsPin.In(gpio.PullNoChange, edge); err != nil { + return p.wrap(err) + } + p.usingEdge = true + } + return nil +} + +// Read implements gpio.PinIn. +// +// This function is fast. It works even if the pin is set as output. +func (p *Pin) Read() gpio.Level { + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return gpio.Low + } + return p.sysfsPin.Read() + } + if p.number < 32 { + // Important: do not remove the &31 here even if not necessary. Testing + // showed that it slows down the performance by several percents. + return gpio.Level((drvGPIO.gpioMemory.level[0] & (1 << uint(p.number&31))) != 0) + } + return gpio.Level((drvGPIO.gpioMemory.level[1] & (1 << uint(p.number&31))) != 0) +} + +// FastRead return the current pin level without any error checking. +// +// This function is very fast. It works even if the pin is set as output. +func (p *Pin) FastRead() gpio.Level { + if p.number < 32 { + // Important: do not remove the &31 here even if not necessary. Testing + // showed that it slows down the performance by several percents. + return gpio.Level((drvGPIO.gpioMemory.level[0] & (1 << uint(p.number&31))) != 0) + } + return gpio.Level((drvGPIO.gpioMemory.level[1] & (1 << uint(p.number&31))) != 0) +} + +// WaitForEdge implements gpio.PinIn. +func (p *Pin) WaitForEdge(timeout time.Duration) bool { + if p.sysfsPin != nil { + return p.sysfsPin.WaitForEdge(timeout) + } + return false +} + +// Pull implements gpio.PinIn. +// +// bcm283x doesn't support querying the pull resistor of any GPIO pin. +func (p *Pin) Pull() gpio.Pull { + // TODO(maruel): The best that could be added is to cache the last set value + // and return it. + return gpio.PullNoChange +} + +// DefaultPull implements gpio.PinIn. +// +// The CPU doesn't return the current pull. +func (p *Pin) DefaultPull() gpio.Pull { + return p.defaultPull +} + +// Out implements gpio.PinOut. +// +// Fails if requesting to change a pin that is set to special functionality. +func (p *Pin) Out(l gpio.Level) error { + if drvGPIO.gpioMemory == nil { + if p.sysfsPin == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized and sysfs not accessible")) + } + return p.sysfsPin.Out(l) + } + // TODO(maruel): This function call is very costly. + if err := p.Halt(); err != nil { + return err + } + // Change output before changing mode to not create any glitch. + p.FastOut(l) + p.setFunction(out) + return nil +} + +// FastOut sets a pin output level with Absolutely No error checking. +// +// Out() Must be called once first before calling FastOut(), otherwise the +// behavior is undefined. Then FastOut() can be used for minimal CPU overhead +// to reach Mhz scale bit banging. +func (p *Pin) FastOut(l gpio.Level) { + mask := uint32(1) << uint(p.number&31) + if l == gpio.Low { + if p.number < 32 { + drvGPIO.gpioMemory.outputClear[0] = mask + } else { + drvGPIO.gpioMemory.outputClear[1] = mask + } + } else { + if p.number < 32 { + drvGPIO.gpioMemory.outputSet[0] = mask + } else { + drvGPIO.gpioMemory.outputSet[1] = mask + } + } +} + +// BUG(maruel): PWM(): There is no conflict verification when multiple pins are +// used simultaneously. The last call to PWM() will affect all pins of the same +// type (CLK0, CLK2, PWM0 or PWM1). + +// PWM implements gpio.PinOut. +// +// It outputs a periodic signal on supported pins without CPU usage. +// +// PWM pins +// +// PWM0 is exposed on pins 12, 18 and 40. However, PWM0 is used for generating +// clock for DMA and unavailable for PWM. +// +// PWM1 is exposed on pins 13, 19, 41 and 45. +// +// PWM1 uses 25Mhz clock source. The frequency must be a divisor of 25Mhz. +// +// DMA driven PWM is available for all pins except PWM1 pins, its resolution is +// 200KHz which is down-sampled from 25MHz clock above. The number of DMA driven +// PWM is limited. +// +// Furthermore, these can only be used if the drive "bcm283x-dma" was loaded. +// It can only be loaded if the process has root level access. +// +// The user must call either Halt(), In(), Out(), PWM(0,..) or +// PWM(gpio.DutyMax,..) to stop the clock source and DMA engine before exiting +// the program. +func (p *Pin) PWM(duty gpio.Duty, freq physic.Frequency) error { + if duty == 0 { + return p.Out(gpio.Low) + } else if duty == gpio.DutyMax { + return p.Out(gpio.High) + } + f := out + useDMA := false + switch p.number { + case 12, 40: // PWM0 alt0: disabled + useDMA = true + case 13, 41, 45: // PWM1 + f = alt0 + case 18: // PWM0 alt5: disabled + useDMA = true + case 19: // PWM1 + f = alt5 + default: + useDMA = true + } + + // Intentionally check later, so a more informative error is returned on + // unsupported pins. + if drvGPIO.gpioMemory == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized")) + } + if drvDMA.pwmMemory == nil || drvDMA.clockMemory == nil { + return p.wrap(errors.New("bcm283x-dma not initialized; try again as root?")) + } + if useDMA { + if m := drvDMA.pwmDMAFreq / 2; m < freq { + return p.wrap(fmt.Errorf("frequency must be at most %s", m)) + } + + // Total cycles in the period + rng := uint64(drvDMA.pwmDMAFreq / freq) + // Pulse width cycles + dat := uint32((rng*uint64(duty) + uint64(gpio.DutyHalf)) / uint64(gpio.DutyMax)) + var err error + // TODO(simokawa): Reuse DMA buffer if possible. + if err = p.haltDMA(); err != nil { + return p.wrap(err) + } + // Start clock before DMA starts. + if _, err = setPWMClockSource(); err != nil { + return p.wrap(err) + } + if p.dmaCh, p.dmaBuf, err = startPWMbyDMA(p, uint32(rng), dat); err != nil { + return p.wrap(err) + } + } else { + if m := drvDMA.pwmBaseFreq / 2; m < freq { + return p.wrap(fmt.Errorf("frequency must be at most %s", m)) + } + // Total cycles in the period + rng := uint64(drvDMA.pwmBaseFreq / freq) + // Pulse width cycles + dat := uint32((rng*uint64(duty) + uint64(gpio.DutyHalf)) / uint64(gpio.DutyMax)) + if _, err := setPWMClockSource(); err != nil { + return p.wrap(err) + } + // Bit shift for PWM0 and PWM1 + shift := uint((p.number & 1) * 8) + if shift == 0 { + drvDMA.pwmMemory.rng1 = uint32(rng) + Nanospin(10 * time.Nanosecond) + drvDMA.pwmMemory.dat1 = uint32(dat) + } else { + drvDMA.pwmMemory.rng2 = uint32(rng) + Nanospin(10 * time.Nanosecond) + drvDMA.pwmMemory.dat2 = uint32(dat) + } + Nanospin(10 * time.Nanosecond) + old := drvDMA.pwmMemory.ctl + drvDMA.pwmMemory.ctl = (old & ^(0xff << shift)) | ((pwm1Enable | pwm1MS) << shift) + } + p.usingClock = true + p.setFunction(f) + return nil +} + +// StreamIn implements gpiostream.PinIn. +// +// DMA driven StreamOut is available for GPIO0 to GPIO31 pin and the maximum +// resolution is 200kHz. +func (p *Pin) StreamIn(pull gpio.Pull, s gpiostream.Stream) error { + b, ok := s.(*gpiostream.BitStream) + if !ok { + return errors.New("bcm283x: other Stream than BitStream are not implemented yet") + } + if !b.LSBF { + return errors.New("bcm283x: MSBF BitStream is not implemented yet") + } + if b.Duration() == 0 { + return errors.New("bcm283x: can't read to empty BitStream") + } + if drvGPIO.gpioMemory == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized")) + } + if err := p.In(pull, gpio.NoEdge); err != nil { + return err + } + if err := dmaReadStream(p, b); err != nil { + return p.wrap(err) + } + return nil +} + +// StreamOut implements gpiostream.PinOut. +// +// I2S/PCM driven StreamOut is available for GPIO21 pin. The resolution is up to +// 250MHz. +// +// For GPIO0 to GPIO31 except GPIO21 pin, DMA driven StreamOut is available and +// the maximum resolution is 200kHz. +func (p *Pin) StreamOut(s gpiostream.Stream) error { + if drvGPIO.gpioMemory == nil { + return p.wrap(errors.New("subsystem gpiomem not initialized")) + } + if err := p.Out(gpio.Low); err != nil { + return err + } + // If the pin is I2S_DOUT, use PCM for much nicer stream and lower memory + // usage. + if p.number == 21 || p.number == 31 { + alt := alt0 + if p.number == 31 { + alt = alt2 + } + p.setFunction(alt) + if err := dmaWriteStreamPCM(p, s); err != nil { + return p.wrap(err) + } + } else if err := dmaWriteStreamEdges(p, s); err != nil { + return p.wrap(err) + } + return nil +} + +// Drive returns the configured output current drive strength for this GPIO. +// +// The current drive is configurable per GPIO groups: 0~27 and 28~45. +// +// The default value for GPIOs 0~27 is 8mA and for GPIOs 28~45 is 16mA. +// +// The value is a multiple 2mA between 2mA and 16mA. +// +// Can only be used if driver bcm283x-dma was loaded. Otherwise returns 0. +func (p *Pin) Drive() physic.ElectricCurrent { + if drvDMA.gpioPadMemory == nil { + return 0 + } + var v pad + if p.number < 28 { + v = drvDMA.gpioPadMemory.pads0 + } else { + // GPIO 46~53 are not exposed. + v = drvDMA.gpioPadMemory.pads1 + } + switch v & 7 { + case padDrive2mA: + return 2 * physic.MilliAmpere + case padDrive4mA: + return 4 * physic.MilliAmpere + case padDrive6mA: + return 6 * physic.MilliAmpere + case padDrive8mA: + return 8 * physic.MilliAmpere + case padDrive10mA: + return 10 * physic.MilliAmpere + case padDrive12mA: + return 12 * physic.MilliAmpere + case padDrive14mA: + return 14 * physic.MilliAmpere + case padDrive16mA: + return 16 * physic.MilliAmpere + default: + return 0 + } +} + +// SlewLimit returns true if the output slew is limited to reduce interference. +// +// The slew is configurable per GPIO groups: 0~27 and 28~45. +// +// The default is true. +// +// Can only be used if driver bcm283x-dma was loaded. Otherwise returns false +// (the default value). +func (p *Pin) SlewLimit() bool { + if drvDMA.gpioPadMemory == nil { + return true + } + if p.number < 28 { + return drvDMA.gpioPadMemory.pads0&padSlewUnlimited == 0 + } + return drvDMA.gpioPadMemory.pads1&padSlewUnlimited == 0 +} + +// Hysteresis returns true if the input hysteresis via a Schmitt trigger is +// enabled. +// +// The hysteresis is configurable per GPIO groups: 0~27 and 28~45. +// +// The default is true. +// +// Can only be used if driver bcm283x-dma was loaded. Otherwise returns true +// (the default value). +func (p *Pin) Hysteresis() bool { + if drvDMA.gpioPadMemory == nil { + return true + } + if p.number < 28 { + return drvDMA.gpioPadMemory.pads0&padHysteresisEnable != 0 + } + return drvDMA.gpioPadMemory.pads1&padHysteresisEnable != 0 +} + +// Internal code. + +func (p *Pin) haltDMA() error { + if p.dmaCh != nil { + p.dmaCh.reset() + p.dmaCh = nil + } + if p.dmaBuf != nil { + if err := p.dmaBuf.Close(); err != nil { + return p.wrap(err) + } + p.dmaBuf = nil + } + return nil +} + +// haltClock disables the CLK/PWM clock if used. +func (p *Pin) haltClock() error { + if err := p.haltDMA(); err != nil { + return err + } + if !p.usingClock { + return nil + } + p.usingClock = false + + // Disable PWMx. + switch p.number { + // PWM0 is not used. + case 12, 18, 40: + // PWM1 + case 13, 19, 41, 45: + for _, i := range []int{13, 19, 41, 45} { + if cpuPins[i].usingClock { + return nil + } + } + shift := uint((p.number & 1) * 8) + drvDMA.pwmMemory.ctl &= ^(0xff << shift) + } + + // Disable PWM clock if nobody use. + for _, pin := range cpuPins { + if pin.usingClock { + return nil + } + } + err := resetPWMClockSource() + return err +} + +// function returns the current GPIO pin function. +func (p *Pin) function() function { + if drvGPIO.gpioMemory == nil { + return alt5 + } + return function((drvGPIO.gpioMemory.functionSelect[p.number/10] >> uint((p.number%10)*3)) & 7) +} + +// setFunction changes the GPIO pin function. +func (p *Pin) setFunction(f function) { + off := p.number / 10 + shift := uint(p.number%10) * 3 + drvGPIO.gpioMemory.functionSelect[off] = (drvGPIO.gpioMemory.functionSelect[off] &^ (7 << shift)) | (uint32(f) << shift) + // If a pin switches from a specific functionality back to GPIO, the alias + // should be updated. For example both GPIO13 and GPIO19 support PWM1. By + // default, PWM1 will be associated to GPIO13, even if + // GPIO19.SetFunc(gpio.PWM) is called. + // TODO(maruel): pinreg.Unregister() + // TODO(maruel): pinreg.Register() +} + +func (p *Pin) wrap(err error) error { + return fmt.Errorf("bcm283x-gpio (%s): %v", p, err) +} + +// + +// Each pin can have one of 7 functions. +const ( + in function = 0 + out function = 1 + alt0 function = 4 + alt1 function = 5 + alt2 function = 6 + alt3 function = 7 + alt4 function = 3 + alt5 function = 2 +) + +// cpuPins is all the pins as supported by the CPU. There is no guarantee that +// they are actually connected to anything on the board. +var cpuPins = []Pin{ + {number: 0, name: "GPIO0", defaultPull: gpio.PullUp}, + {number: 1, name: "GPIO1", defaultPull: gpio.PullUp}, + {number: 2, name: "GPIO2", defaultPull: gpio.PullUp}, + {number: 3, name: "GPIO3", defaultPull: gpio.PullUp}, + {number: 4, name: "GPIO4", defaultPull: gpio.PullUp}, + {number: 5, name: "GPIO5", defaultPull: gpio.PullUp}, + {number: 6, name: "GPIO6", defaultPull: gpio.PullUp}, + {number: 7, name: "GPIO7", defaultPull: gpio.PullUp}, + {number: 8, name: "GPIO8", defaultPull: gpio.PullUp}, + {number: 9, name: "GPIO9", defaultPull: gpio.PullDown}, + {number: 10, name: "GPIO10", defaultPull: gpio.PullDown}, + {number: 11, name: "GPIO11", defaultPull: gpio.PullDown}, + {number: 12, name: "GPIO12", defaultPull: gpio.PullDown}, + {number: 13, name: "GPIO13", defaultPull: gpio.PullDown}, + {number: 14, name: "GPIO14", defaultPull: gpio.PullDown}, + {number: 15, name: "GPIO15", defaultPull: gpio.PullDown}, + {number: 16, name: "GPIO16", defaultPull: gpio.PullDown}, + {number: 17, name: "GPIO17", defaultPull: gpio.PullDown}, + {number: 18, name: "GPIO18", defaultPull: gpio.PullDown}, + {number: 19, name: "GPIO19", defaultPull: gpio.PullDown}, + {number: 20, name: "GPIO20", defaultPull: gpio.PullDown}, + {number: 21, name: "GPIO21", defaultPull: gpio.PullDown}, + {number: 22, name: "GPIO22", defaultPull: gpio.PullDown}, + {number: 23, name: "GPIO23", defaultPull: gpio.PullDown}, + {number: 24, name: "GPIO24", defaultPull: gpio.PullDown}, + {number: 25, name: "GPIO25", defaultPull: gpio.PullDown}, + {number: 26, name: "GPIO26", defaultPull: gpio.PullDown}, + {number: 27, name: "GPIO27", defaultPull: gpio.PullDown}, + {number: 28, name: "GPIO28", defaultPull: gpio.Float}, + {number: 29, name: "GPIO29", defaultPull: gpio.Float}, + {number: 30, name: "GPIO30", defaultPull: gpio.PullDown}, + {number: 31, name: "GPIO31", defaultPull: gpio.PullDown}, + {number: 32, name: "GPIO32", defaultPull: gpio.PullDown}, + {number: 33, name: "GPIO33", defaultPull: gpio.PullDown}, + {number: 34, name: "GPIO34", defaultPull: gpio.PullUp}, + {number: 35, name: "GPIO35", defaultPull: gpio.PullUp}, + {number: 36, name: "GPIO36", defaultPull: gpio.PullUp}, + {number: 37, name: "GPIO37", defaultPull: gpio.PullDown}, + {number: 38, name: "GPIO38", defaultPull: gpio.PullDown}, + {number: 39, name: "GPIO39", defaultPull: gpio.PullDown}, + {number: 40, name: "GPIO40", defaultPull: gpio.PullDown}, + {number: 41, name: "GPIO41", defaultPull: gpio.PullDown}, + {number: 42, name: "GPIO42", defaultPull: gpio.PullDown}, + {number: 43, name: "GPIO43", defaultPull: gpio.PullDown}, + {number: 44, name: "GPIO44", defaultPull: gpio.Float}, + {number: 45, name: "GPIO45", defaultPull: gpio.Float}, + {number: 46, name: "GPIO46", defaultPull: gpio.PullUp}, +} + +// This excludes the functions in and out. +var mapping = [][6]pin.Func{ + {"I2C0_SDA"}, // 0 + {"I2C0_SCL"}, + {"I2C1_SDA"}, + {"I2C1_SCL"}, + {"CLK0"}, + {"CLK1"}, // 5 + {"CLK2"}, + {"SPI0_CS1"}, + {"SPI0_CS0"}, + {"SPI0_MISO"}, + {"SPI0_MOSI"}, // 10 + {"SPI0_CLK"}, + {"PWM0"}, + {"PWM1"}, + {"UART0_TX", "", "", "", "", "UART1_TX"}, + {"UART0_RX", "", "", "", "", "UART1_RX"}, // 15 + {"", "", "", "UART0_CTS", "SPI1_CS2", "UART1_CTS"}, + {"", "", "", "UART0_RTS", "SPI1_CS1", "UART1_RTS"}, + {"I2S_SCK", "", "", "", "SPI1_CS0", "PWM0"}, + {"I2S_WS", "", "", "", "SPI1_MISO", "PWM1"}, + {"I2S_DIN", "", "", "", "SPI1_MOSI", "CLK0"}, // 20 + {"I2S_DOUT", "", "", "", "SPI1_CLK", "CLK1"}, + {""}, + {""}, + {""}, + {""}, // 25 + {""}, + {""}, + {"I2C0_SDA", "", "I2S_SCK", "", "", ""}, + {"I2C0_SCL", "", "I2S_WS", "", "", ""}, + {"", "", "I2S_DIN", "UART0_CTS", "", "UART1_CTS"}, // 30 + {"", "", "I2S_DOUT", "UART0_RTS", "", "UART1_RTS"}, + {"CLK0", "", "", "UART0_TX", "", "UART1_TX"}, + {"", "", "", "UART0_RX", "", "UART1_RX"}, + {"CLK0"}, + {"SPI0_CS1"}, // 35 + {"SPI0_CS0", "", "UART0_TX", "", "", ""}, + {"SPI0_MISO", "", "UART0_RX", "", "", ""}, + {"SPI0_MOSI", "", "UART0_RTS", "", "", ""}, + {"SPI0_CLK", "", "UART0_CTS", "", "", ""}, + {"PWM0", "", "", "", "SPI2_MISO", "UART1_TX"}, // 40 + {"PWM1", "", "", "", "SPI2_MOSI", "UART1_RX"}, + {"CLK1", "", "", "", "SPI2_CLK", "UART1_RTS"}, + {"CLK2", "", "", "", "SPI2_CS0", "UART1_CTS"}, + {"CLK1", "I2C0_SDA", "I2C1_SDA", "", "SPI2_CS1", ""}, + {"PWM1", "I2C0_SCL", "I2C1_SCL", "", "SPI2_CS2", ""}, // 45 + {""}, +} + +// function specifies the active functionality of a pin. The alternative +// function is GPIO pin dependent. +type function uint8 + +// Mapping as +// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +// pages 90-91. +type gpioMap struct { + // 0x00 RW GPIO Function Select 0 (GPIO0-9) + // 0x04 RW GPIO Function Select 1 (GPIO10-19) + // 0x08 RW GPIO Function Select 2 (GPIO20-29) + // 0x0C RW GPIO Function Select 3 (GPIO30-39) + // 0x10 RW GPIO Function Select 4 (GPIO40-49) + // 0x14 RW GPIO Function Select 5 (GPIO50-53) + functionSelect [6]uint32 // GPFSEL0~GPFSEL5 + // 0x18 - Reserved + dummy0 uint32 + // 0x1C W GPIO Pin Output Set 0 (GPIO0-31) + // 0x20 W GPIO Pin Output Set 1 (GPIO32-53) + outputSet [2]uint32 // GPSET0-GPSET1 + // 0x24 - Reserved + dummy1 uint32 + // 0x28 W GPIO Pin Output Clear 0 (GPIO0-31) + // 0x2C W GPIO Pin Output Clear 1 (GPIO32-53) + outputClear [2]uint32 // GPCLR0-GPCLR1 + // 0x30 - Reserved + dummy2 uint32 + // 0x34 R GPIO Pin Level 0 (GPIO0-31) + // 0x38 R GPIO Pin Level 1 (GPIO32-53) + level [2]uint32 // GPLEV0-GPLEV1 + // 0x3C - Reserved + dummy3 uint32 + // 0x40 RW GPIO Pin Event Detect Status 0 (GPIO0-31) + // 0x44 RW GPIO Pin Event Detect Status 1 (GPIO32-53) + eventDetectStatus [2]uint32 // GPEDS0-GPEDS1 + // 0x48 - Reserved + dummy4 uint32 + // 0x4C RW GPIO Pin Rising Edge Detect Enable 0 (GPIO0-31) + // 0x50 RW GPIO Pin Rising Edge Detect Enable 1 (GPIO32-53) + risingEdgeDetectEnable [2]uint32 // GPREN0-GPREN1 + // 0x54 - Reserved + dummy5 uint32 + // 0x58 RW GPIO Pin Falling Edge Detect Enable 0 (GPIO0-31) + // 0x5C RW GPIO Pin Falling Edge Detect Enable 1 (GPIO32-53) + fallingEdgeDetectEnable [2]uint32 // GPFEN0-GPFEN1 + // 0x60 - Reserved + dummy6 uint32 + // 0x64 RW GPIO Pin High Detect Enable 0 (GPIO0-31) + // 0x68 RW GPIO Pin High Detect Enable 1 (GPIO32-53) + highDetectEnable [2]uint32 // GPHEN0-GPHEN1 + // 0x6C - Reserved + dummy7 uint32 + // 0x70 RW GPIO Pin Low Detect Enable 0 (GPIO0-31) + // 0x74 RW GPIO Pin Low Detect Enable 1 (GPIO32-53) + lowDetectEnable [2]uint32 // GPLEN0-GPLEN1 + // 0x78 - Reserved + dummy8 uint32 + // 0x7C RW GPIO Pin Async Rising Edge Detect 0 (GPIO0-31) + // 0x80 RW GPIO Pin Async Rising Edge Detect 1 (GPIO32-53) + asyncRisingEdgeDetectEnable [2]uint32 // GPAREN0-GPAREN1 + // 0x84 - Reserved + dummy9 uint32 + // 0x88 RW GPIO Pin Async Falling Edge Detect 0 (GPIO0-31) + // 0x8C RW GPIO Pin Async Falling Edge Detect 1 (GPIO32-53) + asyncFallingEdgeDetectEnable [2]uint32 // GPAFEN0-GPAFEN1 + // 0x90 - Reserved + dummy10 uint32 + // 0x94 RW GPIO Pin Pull-up/down Enable (00=Float, 01=Down, 10=Up) + pullEnable uint32 // GPPUD + // 0x98 RW GPIO Pin Pull-up/down Enable Clock 0 (GPIO0-31) + // 0x9C RW GPIO Pin Pull-up/down Enable Clock 1 (GPIO32-53) + pullEnableClock [2]uint32 // GPPUDCLK0-GPPUDCLK1 + // 0xA0 - Reserved + dummy uint32 + // 0xB0 - Test (byte) +} + +// pad defines the settings for a GPIO pad group. +type pad uint32 + +const ( + padPasswd pad = 0x5A << 24 // Write protection + padSlewUnlimited pad = 1 << 4 // Output bandwidth limit to reduce bounce. + padHysteresisEnable pad = 1 << 3 // Schmitt trigger + padDrive2mA pad = 0 + padDrive4mA pad = 1 + padDrive6mA pad = 2 + padDrive8mA pad = 3 + padDrive10mA pad = 4 + padDrive12mA pad = 5 + padDrive14mA pad = 6 + padDrive16mA pad = 7 +) + +// set changes the current drive strength for the GPIO pad group. +// +// We could disable the schmitt trigger or the slew limit. +func (p *pad) set(settings pad) { + *p = padPasswd | settings +} + +func toPad(drive physic.ElectricCurrent, slewLimit, hysteresis bool) pad { + var p pad + d := int(drive / physic.MilliAmpere) + switch { + case d <= 2: + p = padDrive2mA + case d <= 4: + p = padDrive4mA + case d <= 6: + p = padDrive6mA + case d <= 8: + p = padDrive8mA + case d <= 10: + p = padDrive10mA + case d <= 12: + p = padDrive12mA + case d <= 14: + p = padDrive14mA + default: + p = padDrive16mA + } + if !slewLimit { + p |= padSlewUnlimited + } + if hysteresis { + p |= padHysteresisEnable + } + return p +} + +// Mapping as https://scribd.com/doc/101830961/GPIO-Pads-Control2 +type gpioPadMap struct { + dummy [11]uint32 // 0x00~0x28 + pads0 pad // 0x2c GPIO 0~27 + pads1 pad // 0x30 GPIO 28~45 + pads2 pad // 0x34 GPIO 46~53 +} + +func init() { + GPIO0 = &cpuPins[0] + GPIO1 = &cpuPins[1] + GPIO2 = &cpuPins[2] + GPIO3 = &cpuPins[3] + GPIO4 = &cpuPins[4] + GPIO5 = &cpuPins[5] + GPIO6 = &cpuPins[6] + GPIO7 = &cpuPins[7] + GPIO8 = &cpuPins[8] + GPIO9 = &cpuPins[9] + GPIO10 = &cpuPins[10] + GPIO11 = &cpuPins[11] + GPIO12 = &cpuPins[12] + GPIO13 = &cpuPins[13] + GPIO14 = &cpuPins[14] + GPIO15 = &cpuPins[15] + GPIO16 = &cpuPins[16] + GPIO17 = &cpuPins[17] + GPIO18 = &cpuPins[18] + GPIO19 = &cpuPins[19] + GPIO20 = &cpuPins[20] + GPIO21 = &cpuPins[21] + GPIO22 = &cpuPins[22] + GPIO23 = &cpuPins[23] + GPIO24 = &cpuPins[24] + GPIO25 = &cpuPins[25] + GPIO26 = &cpuPins[26] + GPIO27 = &cpuPins[27] + GPIO28 = &cpuPins[28] + GPIO29 = &cpuPins[29] + GPIO30 = &cpuPins[30] + GPIO31 = &cpuPins[31] + GPIO32 = &cpuPins[32] + GPIO33 = &cpuPins[33] + GPIO34 = &cpuPins[34] + GPIO35 = &cpuPins[35] + GPIO36 = &cpuPins[36] + GPIO37 = &cpuPins[37] + GPIO38 = &cpuPins[38] + GPIO39 = &cpuPins[39] + GPIO40 = &cpuPins[40] + GPIO41 = &cpuPins[41] + GPIO42 = &cpuPins[42] + GPIO43 = &cpuPins[43] + GPIO44 = &cpuPins[44] + GPIO45 = &cpuPins[45] + GPIO46 = &cpuPins[46] +} + +// Changing pull resistor require a 150 cycles sleep. +// +// Do not inline so the temporary value is not optimized out. +// +//go:noinline +func sleep150cycles() uint32 { + // Do not call into any kernel function, since this causes a high chance of + // being preempted. + // Abuse the fact that gpioMemory is uncached memory. + // TODO(maruel): No idea if this is too much or enough. + var out uint32 + for i := 0; i < 150; i++ { + out += drvGPIO.gpioMemory.functionSelect[0] + } + return out +} + +// driverGPIO implements periph.Driver. +type driverGPIO struct { + // baseAddr is the base for all the CPU registers. + // + // It is initialized by driverGPIO.Init(). + baseAddr uint32 + // dramBus is high bits to address uncached memory. See virtToUncachedPhys() + // in dma.go. + dramBus uint32 + // gpioMemory is the memory map of the CPU GPIO registers. + gpioMemory *gpioMap + // gpioBaseAddr is needed for DMA transfers. + gpioBaseAddr uint32 +} + +func (d *driverGPIO) Close() { + d.baseAddr = 0 + d.dramBus = 0 + d.gpioMemory = nil + d.gpioBaseAddr = 0 +} + +func (d *driverGPIO) String() string { + return "bcm283x-gpio" +} + +func (d *driverGPIO) Prerequisites() []string { + return nil +} + +func (d *driverGPIO) After() []string { + return []string{"sysfs-gpio"} +} + +func (d *driverGPIO) Init() (bool, error) { + if !Present() { + return false, errors.New("bcm283x CPU not detected") + } + model := distro.CPUInfo()["model name"] + if strings.Contains(model, "ARMv6") { + d.baseAddr = 0x20000000 + d.dramBus = 0x40000000 + } else { + // RPi2+ + d.baseAddr = 0x3F000000 + d.dramBus = 0xC0000000 + } + // Page 6. + // Virtual addresses in kernel mode will range between 0xC0000000 and + // 0xEFFFFFFF. + // Virtual addresses in user mode (i.e. seen by processes running in ARM + // Linux) will range between 0x00000000 and 0xBFFFFFFF. + // Peripherals (at physical address 0x20000000 on) are mapped into the kernel + // virtual address space starting at address 0xF2000000. Thus a peripheral + // advertised here at bus address 0x7Ennnnnn is available in the ARM kenel at + // virtual address 0xF2nnnnnn. + d.gpioBaseAddr = d.baseAddr + 0x200000 + + // Mark the right pins as available even if the memory map fails so they can + // callback to sysfs.Pins. + functions := map[pin.Func]struct{}{} + for i := range cpuPins { + name := cpuPins[i].name + num := strconv.Itoa(cpuPins[i].number) + + // Initializes the sysfs corresponding pin right away. + cpuPins[i].sysfsPin = sysfs.Pins[cpuPins[i].number] + + // Unregister the pin if already registered. This happens with sysfs-gpio. + // Do not error on it, since sysfs-gpio may have failed to load. + _ = gpioreg.Unregister(name) + _ = gpioreg.Unregister(num) + + if err := gpioreg.Register(&cpuPins[i]); err != nil { + return true, err + } + if err := gpioreg.RegisterAlias(num, name); err != nil { + return true, err + } + switch f := cpuPins[i].Func(); f { + case gpio.IN, gpio.OUT: + default: + // Registering the same alias twice fails. This can happen if two pins + // are configured with the same function. For example both pin #12, #18 + // and #40 could be configured to work as PWM0. + if _, ok := functions[f]; !ok { + functions[f] = struct{}{} + if err := gpioreg.RegisterAlias(string(f), name); err != nil { + return true, err + } + } + } + } + + // Now do a second loop but do the alternate functions. + for i := range cpuPins { + for _, f := range cpuPins[i].SupportedFuncs() { + switch f { + case gpio.IN, gpio.OUT: + default: + if _, ok := functions[f]; !ok { + functions[f] = struct{}{} + if err := gpioreg.RegisterAlias(string(f), cpuPins[i].name); err != nil { + return true, err + } + } + } + } + } + + // Register some BCM-documentation specific names. + // Do not do UARTx_TXD/RXD nor the PCM_xxx ones. + aliases := [][2]string{ + {"GPCLK0", "CLK0"}, + {"GPCLK1", "CLK1"}, + {"GPCLK2", "CLK2"}, + {"PWM0_OUT", "PWM0"}, + {"PWM1_OUT", "PWM1"}, + } + for _, a := range aliases { + if err := gpioreg.RegisterAlias(a[0], a[1]); err != nil { + return true, err + } + } + + m, err := pmem.MapGPIO() + if err != nil { + // Try without /dev/gpiomem. This is the case of not running on Raspbian or + // raspbian before Jessie. This requires running as root. + var err2 error + m, err2 = pmem.Map(uint64(d.gpioBaseAddr), 4096) + var err error + if err2 != nil { + if distro.IsRaspbian() { + // Raspbian specific error code to help guide the user to troubleshoot + // the problems. + if os.IsNotExist(err) && os.IsPermission(err2) { + return true, fmt.Errorf("/dev/gpiomem wasn't found; please upgrade to Raspbian Jessie or run as root") + } + } + if os.IsPermission(err2) { + return true, fmt.Errorf("need more access, try as root: %v", err) + } + return true, err + } + } + if err := m.AsPOD(&d.gpioMemory); err != nil { + return true, err + } + + return true, sysfs.I2CSetSpeedHook(setSpeed) +} + +func setSpeed(f physic.Frequency) error { + // Writing to "/sys/module/i2c_bcm2708/parameters/baudrate" was confirmed to + // not work. + // modprobe hangs when a bus is opened, so this must be called *before* the + // bus is opened. + // TL;DR: we can't do anything here. + /* + if err := exec.Command("modprobe", "-r", "i2c_bcm2708").Run(); err != nil { + return fmt.Errorf("bcm283x: failed to unload driver i2c_bcm2708: %v", err) + } + if err := exec.Command("modprobe", "i2c_bcm2708", "baudrate=600000"); err != nil { + return fmt.Errorf("bcm283x: failed to reload driver i2c_bcm2708: %v", err) + } + */ + return errors.New("bcm283x: to change the I²C bus speed, please refer to https://periph.io/platform/raspberrypi/#i²c") +} + +func init() { + if isArm { + periph.MustRegister(&drvGPIO) + } +} + +var drvGPIO driverGPIO + +var _ gpio.PinIO = &Pin{} +var _ gpio.PinIn = &Pin{} +var _ gpio.PinOut = &Pin{} +var _ gpiostream.PinIn = &Pin{} +var _ gpiostream.PinOut = &Pin{} +var _ pin.PinFunc = &Pin{} diff --git a/vendor/periph.io/x/periph/host/bcm283x/pcm.go b/vendor/periph.io/x/periph/host/bcm283x/pcm.go new file mode 100644 index 0000000..7078de1 --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/pcm.go @@ -0,0 +1,234 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// pcm means I2S. + +package bcm283x + +import ( + "errors" + "fmt" + "time" + + "periph.io/x/periph/conn/physic" +) + +type pcmCS uint32 + +// Pages 126-129 +const ( + // 31:26 reserved + pcmStandby pcmCS = 1 << 25 // STBY Allow at least 4 PCM clock cycles to take effect + pcmSync pcmCS = 1 << 24 // SYNC Two PCM clocks have occurred since last write + pcmRXSignExtend pcmCS = 1 << 23 // RXSEX Sign extend RXZ data + pcmRXFull pcmCS = 1 << 22 // RXF RX FIFO is full + pcmTXEmpty pcmCS = 1 << 21 // TXE TX FIFO is empty + pcmRXData pcmCS = 1 << 20 // RXD RX FIFO contains data + pcmTXData pcmCS = 1 << 19 // TXD TX FIFO ready to accept data + pcmRXR pcmCS = 1 << 18 // RXR RX FIFO needs reading + pcmTXW pcmCS = 1 << 17 // TXW TX FIFO needs writing + pcmRXErr pcmCS = 1 << 16 // RXERR RX FIFO error + pcmTXErr pcmCS = 1 << 15 // TXERR TX FIFO error + pcmRXSync pcmCS = 1 << 14 // RXSYNC RX FIFO is out of sync + pcmTXSync pcmCS = 1 << 13 // TXSYNC TX FIFO is out of sync + // 12:10 reserved + pcmDMAEnable pcmCS = 1 << 9 // DMAEN Generate TX&RX DMA DREQ + // 8:7 RXTHR controls when pcmRXR is set + pcmRXThresholdOne pcmCS = 0 << 7 // One sample in RX FIFO + pcmRXThreshold1 pcmCS = 1 << 7 // RX FIFO is at least (?) full + pcmRXThreshold2 pcmCS = 2 << 7 // ? + pcmRXThresholdFull pcmCS = 3 << 7 // RX is full + // 6:5 TXTHR controls when pcmTXW is set + pcmTXThresholdEmpty pcmCS = 0 << 5 // TX FIFO is empty + pcmTXThresholdNotFull1 pcmCS = 1 << 5 // At least one sample can be put + pcmTXThresholdNotFull2 pcmCS = 2 << 5 // At least one sample can be put + pcmTXThresholdOne pcmCS = 3 << 5 // One sample can be put + pcmRXClear pcmCS = 1 << 4 // RXCLR Clear RX FIFO; takes 2 PCM clock to take effect + pcmTXClear pcmCS = 1 << 3 // TXCLR Clear TX FIFO; takes 2 PCM clock to take effect + pcmTXEnable pcmCS = 1 << 2 // TXON Enable TX + pcmRXEnable pcmCS = 1 << 1 // RXON Enable FX + pcmEnable pcmCS = 1 << 0 // EN Enable the PCM +) + +type pcmMode uint32 + +// Page 129-131 +const ( + // 31:29 reserved + pcmClockDisable pcmMode = 1 << 28 // CLK_DIS Cleanly disable the PCM clock + pcmDecimation32 pcmMode = 1 << 27 // PDMN; 0 is factor 16, 1 is factor 32 + pcmRXPDMFilter pcmMode = 1 << 26 // PDME Enable input CIC filter on PDM input + pcmRXMerge pcmMode = 1 << 25 // FRXP Merge both channels as single FIFO entry + pcmTXMerge pcmMode = 1 << 24 // FTXP Merge both channels as singe FIFO entry + pcmClockSlave pcmMode = 1 << 23 // CLKM PCM CLK is input + pcmClockInverted pcmMode = 1 << 22 // CLKI Inverse clock signal + pcmFSSlave pcmMode = 1 << 21 // FSM PCM FS is input + pcmFSInverted pcmMode = 1 << 20 // FSI Invese FS signal + pcmFrameLengthShift = 10 // + pcmFrameLenghtMask pcmMode = 0x3F << pcmFrameLengthShift // FLEN Frame length + 1 + pcmFSLenghtMask pcmMode = 0x3F << 0 // FSLEN FS pulse clock width +) + +type pcmRX uint32 + +// Page 131-132 +const ( + pcmRX1Width pcmRX = 1 << 31 // CH1WEX Legacy + pcmRX1Enable pcmRX = 1 << 30 // CH1EN + pcmRX1PosShift = 20 + pcmRX1PosMask pcmRX = 0x3F << pcmRX1PosShift // CH1POS Clock delay + pcmRX1Channel16 pcmRX = 8 << 16 // CH1WID (Arbitrary width between 8 and 16 is supported) + pcmRX2Width pcmRX = 1 << 15 // CH2WEX Legacy + pcmRX2Enable pcmRX = 1 << 14 // CH2EN + pcmRX2PosShift = 4 + pcmRX2PosMask pcmRX = 0x3F << pcmRX2PosShift // CH2POS Clock delay + pcmRX2Channel16 pcmRX = 8 << 0 // CH2WID (Arbitrary width between 8 and 16 is supported) +) + +type pcmTX uint32 + +// Page 133-134 +const ( + pcmTX1Width pcmTX = 1 << 31 // CH1WX Legacy + pcmTX1Enable pcmTX = 1 << 30 // CH1EN Enable channel 1 + pcmTX1PosShift = 20 + pcmTX1PosMask pcmTX = 0x3F << pcmTX1PosShift // CH1POS Clock delay + pcmTX1Channel16 pcmTX = 8 << 16 // CH1WID (Arbitrary width between 8 and 16 is supported) + pcmTX2Width pcmTX = 1 << 15 // CH2WEX Legacy + pcmTX2Enable pcmTX = 1 << 14 // CH2EN + pcmTX2PosShift = 4 + pcmTX2PosMask pcmTX = 0x3F << pcmTX2PosShift // CH2POS Clock delay + pcmTX2Channel16 pcmTX = 8 << 0 // CH2WID (Arbitrary width between 8 and 16 is supported) +) + +type pcmDreq uint32 + +// Page 134-135 +const ( + // 31 reserved + pcmDreqTXPanicShift = 24 + pcmDreqTXPanicMask pcmDreq = 0x7F << pcmDreqTXPanicShift // TX_PANIC Panic level + // 23 reserved + pcmDreqRXPanicShift = 16 + pcmDreqRXPanicMask pcmDreq = 0x7F << pcmDreqRXPanicShift // RX_PANIC Panic level + // 15 reserved + pcmDreqTXLevelShift = 8 + pcmDreqTXLevelMask pcmDreq = 0x7F << pcmDreqTXPanicShift // TX Request Level + // 7 reserved + pcmDreqRXLevelShift = 0 + pcmDreqRXLevelMask pcmDreq = 0x7F << pcmDreqRXPanicShift // RX Request Level +) + +type pcmInterrupt uint32 + +// Page 135 +const ( + // 31:4 reserved + pcmIntRXErr pcmInterrupt = 1 << 3 // RXERR RX error interrupt enable + pcmIntTXErr pcmInterrupt = 1 << 2 // TXERR TX error interrupt enable + pcmIntRXEnable pcmInterrupt = 1 << 1 // RXR RX Read interrupt enable + pcmIntTXEnable pcmInterrupt = 1 << 0 // TXW TX Write interrupt enable +) + +type pcmIntStatus uint32 + +// Page 135-136 +const ( + // 31:4 reserved + pcmIntStatRXErr pcmIntStatus = 1 << 3 // RXERR RX error occurred / clear + pcmIntStatTXErr pcmIntStatus = 1 << 2 // TXERR TX error occurred / clear + pcmIntStatRXEnable pcmIntStatus = 1 << 1 // RXR RX Read interrupt occurred / clear + pcmIntStatTXEnable pcmIntStatus = 1 << 0 // TXW TX Write interrupt occurred / clear + pcmIntStatusClear pcmIntStatus = 0xF +) + +// pcmGray puts it into a special data/strobe mode that is under 'best effort' +// contract. +type pcmGray uint32 + +// Page 136-137 +const ( + // 31:22 reserved + pcmGrayRXFIFOLevelShift = 16 + pcmGrayRXFIFOLevelMask pcmGray = 0x3F << pcmGrayRXFIFOLevelShift // RXFIFOLEVEL How many words in RXFIFO + pcmGrayFlushShift = 10 + pcmGrayFlushMask = 0x3F << pcmGrayFlushShift // FLUSHED How many bits were valid when flush occurred + pcmGrayRXLevelShift = 4 + pcmGrayRXLevelMask pcmGray = 0x3F << pcmGrayRXLevelShift // RXLEVEL How many GRAY coded bits received + pcmGrayFlush pcmGray = 1 << 2 // FLUSH + pcmGrayClear pcmGray = 1 << 1 // CLR + pcmGrayEnable pcmGray = 1 << 0 // EN +) + +// Page 119 +type pcmMap struct { + cs pcmCS // CS_A Control Status + fifo uint32 // FIFO_A FIFO register + mode pcmMode // MODE_A Operation mode + rxc pcmRX // RXC_A RX control + txc pcmTX // TXC_A TX control + dreq pcmDreq // DREQ_A DMA control + inten pcmInterrupt // INTEN_A Interrupt enable + intstc pcmIntStatus // INTSTC_A Interrupt status + gray pcmGray // GRAY Gray mode input processing +} + +func (p *pcmMap) GoString() string { + return fmt.Sprintf( + "{\n cs: 0x%x,\n mode: 0x%x,\n rxc: 0x%x,\n txc: 0x%x,\n dreq: 0x%x,\n inten: 0x%x,\n intstc: 0x%x,\n gray: 0x%x,\n}", + p.cs, p.mode, p.rxc, p.txc, p.dreq, p.inten, p.intstc, p.gray) +} + +func (p *pcmMap) reset() { + p.cs = 0 + // In theory need to wait the equivalent of 2 PCM clocks. + // TODO(maruel): Use pcmSync busy loop to synchronize. + Nanospin(time.Microsecond) + // Hard reset + p.fifo = 0 + p.mode = 0 + p.rxc = 0 + p.txc = 0 + p.dreq = 0 + p.inten = 0 + p.intstc = pcmIntStatusClear + p.gray = 0 + + // Clear pcmStandby / pcm +} + +// set initializes 8 bits stream via DMA with no delay and no FS. +func (p *pcmMap) set() { + p.cs |= pcmEnable + p.txc = pcmTX1Width | pcmTX1Channel16 | pcmTX1Enable // 32bit TX + p.mode = (32 - 1) << pcmFrameLengthShift + p.cs |= pcmTXClear | pcmRXClear + // In theory need to wait the equivalent of 2 PCM clocks. + // TODO(maruel): Use pcmSync busy loop to synchronize. + Nanospin(time.Microsecond) + p.dreq = 0x10<>bit<<0 | + (d[skip*1]&mask)>>bit<<1 | + (d[skip*2]&mask)>>bit<<2 | + (d[skip*3]&mask)>>bit<<3 | + (d[skip*4]&mask)>>bit<<4 | + (d[skip*5]&mask)>>bit<<5 | + (d[skip*6]&mask)>>bit<<6 | + (d[skip*7]&mask)>>bit<<7) + d = d[skip*8:] + } +} + +func getBit(b byte, index int, msb bool) byte { + var shift uint + if msb { + shift = uint(7 - index) + } else { + shift = uint(index) + } + return (b >> shift) & 1 +} + +func raster32Bits(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error { + var msb bool + var bits []byte + switch b := s.(type) { + case *gpiostream.BitStream: + msb = !b.LSBF + bits = b.Bits + default: + return fmt.Errorf("Unsupported type %T", b) + } + m := len(clear) / 8 + if n := len(bits); n < m { + m = n + } + index := 0 + for i := 0; i < m; i++ { + for j := 0; j < 8; j++ { + if getBit(bits[i], j, msb) != 0 { + for k := 0; k < skip; k++ { + set[index] |= mask + index++ + } + } else { + for k := 0; k < skip; k++ { + clear[index] |= mask + index++ + } + } + } + } + return nil +} + +// raster32 rasters the stream into a uint32 stream with the specified masks to +// put in the correctly slice when the bit is set and when it is clear. +// +// `s` must be one of the types in this package. +func raster32(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error { + if mask == 0 { + return errors.New("bcm283x: mask is 0") + } + if len(clear) == 0 { + return errors.New("bcm283x: clear buffer is empty") + } + if len(set) == 0 { + return errors.New("bcm283x: set buffer is empty") + } + if len(clear) != len(set) { + return errors.New("bcm283x: clear and set buffers have different length") + } + switch x := s.(type) { + case *gpiostream.BitStream: + // TODO + return raster32Bits(x, skip, clear, set, mask) + case *gpiostream.EdgeStream: + return errors.New("bcm283x: EdgeStream is not supported yet") + case *gpiostream.Program: + return errors.New("bcm283x: Program is not supported yet") + default: + return errors.New("bcm283x: unknown stream type") + } +} + +// PCM/PWM DMA buf is encoded as little-endian and MSB first. +func copyStreamToDMABuf(w gpiostream.Stream, dst []uint32) error { + switch v := w.(type) { + case *gpiostream.BitStream: + if v.LSBF { + return errors.New("TODO(simokawa): handle BitStream.LSBF") + } + // This is big-endian and MSB first. + i := 0 + for ; i < len(v.Bits)/4; i++ { + dst[i] = binary.BigEndian.Uint32(v.Bits[i*4:]) + } + last := uint32(0) + if mod := len(v.Bits) % 4; mod > 0 { + for j := 0; j < mod; j++ { + last |= (uint32(v.Bits[i*4+j])) << uint32(8*(3-j)) + } + dst[i] = last + } + return nil + case *gpiostream.EdgeStream: + return errors.New("TODO(simokawa): handle EdgeStream") + default: + return errors.New("Unsupported Stream type") + } +} diff --git a/vendor/periph.io/x/periph/host/bcm283x/timer.go b/vendor/periph.io/x/periph/host/bcm283x/timer.go new file mode 100644 index 0000000..4c6247e --- /dev/null +++ b/vendor/periph.io/x/periph/host/bcm283x/timer.go @@ -0,0 +1,60 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package bcm283x + +import ( + "time" + + "periph.io/x/periph/host/cpu" +) + +// ReadTime returns the time on a monotonic 1Mhz clock (1µs resolution). +// +// It only works if bcm283x-dma successfully loaded. Otherwise it returns 0. +func ReadTime() time.Duration { + if drvDMA.timerMemory == nil { + return 0 + } + return (time.Duration(drvDMA.timerMemory.high)<<32 | time.Duration(drvDMA.timerMemory.low)) * time.Microsecond +} + +// Nanospin spins the CPU without calling into the kernel code if possible. +func Nanospin(t time.Duration) { + start := ReadTime() + if start == 0 { + // Use the slow generic version. + cpu.Nanospin(t) + return + } + // TODO(maruel): Optimize code path for sub-1µs duration. + for ReadTime()-start < t { + } +} + +// + +const ( + // 31:4 reserved + timerM3 = 1 << 3 // M3 + timerM2 = 1 << 2 // M2 + timerM1 = 1 << 1 // M1 + timerM0 = 1 << 0 // M0 +) + +// Page 173 +type timerCtl uint32 + +// timerMap represents the registers to access the 1Mhz timer. +// +// Page 172 +type timerMap struct { + ctl timerCtl // CS + low uint32 // CLO + high uint32 // CHI + c0 uint32 // 0 + c1 uint32 // C1 + c2 uint32 // C2 + c3 uint32 // C3 +} diff --git a/vendor/periph.io/x/periph/host/beagle/black/black.go b/vendor/periph.io/x/periph/host/beagle/black/black.go new file mode 100644 index 0000000..1ff0317 --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/black/black.go @@ -0,0 +1,34 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package black implements headers for the BeagleBone Black and BeagleBone +// Black Wireless micro-computers. +// +// Reference +// +// https://beagleboard.org/black +// +// Datasheet +// +// https://elinux.org/Beagleboard:BeagleBoneBlack +// +// https://github.com/CircuitCo/BeagleBone-Black/blob/rev_b/BBB_SRM.pdf +// +// https://elinux.org/Beagleboard:Cape_Expansion_Headers +package black + +import ( + "strings" + + "periph.io/x/periph/host/distro" +) + +// Present returns true if the host is a BeagleBone Black or BeagleBone Black +// Wireless. +func Present() bool { + if isArm { + return strings.HasPrefix(distro.DTModel(), "TI AM335x BeagleBone Black") + } + return false +} diff --git a/vendor/periph.io/x/periph/host/beagle/black/black_arm.go b/vendor/periph.io/x/periph/host/beagle/black/black_arm.go new file mode 100644 index 0000000..9c85af9 --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/black/black_arm.go @@ -0,0 +1,7 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package black + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/beagle/black/black_other.go b/vendor/periph.io/x/periph/host/beagle/black/black_other.go new file mode 100644 index 0000000..1c146a7 --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/black/black_other.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm + +package black + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/beagle/bone/bone.go b/vendor/periph.io/x/periph/host/beagle/bone/bone.go new file mode 100644 index 0000000..356e587 --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/bone/bone.go @@ -0,0 +1,318 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package bone implements headers J1, P8 and P9 found on many (but not all) +// BeagleBone micro-computer. +// +// In particular, the headers are found on the models using a TI AM335x +// processor: BeagleBone Black, Black Wireless, Green and Green Wireless. +// +// Reference +// +// http://beagleboard.org/Support/bone101/#hardware +package bone + +import ( + "errors" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/conn/pin/pinreg" + "periph.io/x/periph/host/beagle/black" + "periph.io/x/periph/host/beagle/green" + "periph.io/x/periph/host/sysfs" +) + +// TODO(maruel): Use specialized am335x or pru implementation once available. + +// Common pin types on BeagleBones. +var ( + PWR_BUT = &pin.BasicPin{N: "PWR_BUT"} // + RESET_OUT = &pin.BasicPin{N: "RESET_OUT"} // SYS_RESETn + VADC = &pin.BasicPin{N: "VADC"} // VDD_ADC + AIN4 = &pin.BasicPin{N: "AIN4"} // AIN4 + AGND = &pin.BasicPin{N: "AGND"} // GNDA_ADC + AIN6 = &pin.BasicPin{N: "AIN6"} // AIN6 + AIN5 = &pin.BasicPin{N: "AIN5"} // AIN5 + AIN2 = &pin.BasicPin{N: "AIN2"} // AIN2 + AIN3 = &pin.BasicPin{N: "AIN3"} // AIN3 + AIN0 = &pin.BasicPin{N: "AIN0"} // AIN0 + AIN1 = &pin.BasicPin{N: "AIN1"} // AIN1 +) + +// Headers found on BeagleBones. +var ( + // Port J1 is the UART port where the default terminal is connected to. + J1_1 pin.Pin = pin.GROUND + J1_2 pin.Pin = pin.INVALID + J1_3 pin.Pin = pin.INVALID + J1_4 gpio.PinIO = gpio.INVALID // GPIO42, UART0_RX + J1_5 gpio.PinIO = gpio.INVALID // GPIO43, UART0_TX + J1_6 pin.Pin = pin.INVALID + + P8_1 pin.Pin = pin.GROUND + P8_2 pin.Pin = pin.GROUND + P8_3 gpio.PinIO = gpio.INVALID // GPIO38, MMC1_DAT6 + P8_4 gpio.PinIO = gpio.INVALID // GPIO39, MMC1_DAT7 + P8_5 gpio.PinIO = gpio.INVALID // GPIO34, MMC1_DAT2 + P8_6 gpio.PinIO = gpio.INVALID // GPIO35, MMC1_DAT3 + P8_7 gpio.PinIO = gpio.INVALID // GPIO66, Timer4 + P8_8 gpio.PinIO = gpio.INVALID // GPIO67, Timer7 + P8_9 gpio.PinIO = gpio.INVALID // GPIO69, Timer5 + P8_10 gpio.PinIO = gpio.INVALID // GPIO68, Timer6 + P8_11 gpio.PinIO = gpio.INVALID // GPIO45, + P8_12 gpio.PinIO = gpio.INVALID // GPIO44, + P8_13 gpio.PinIO = gpio.INVALID // GPIO23, EHRPWM2B + P8_14 gpio.PinIO = gpio.INVALID // GPIO26, + P8_15 gpio.PinIO = gpio.INVALID // GPIO47, + P8_16 gpio.PinIO = gpio.INVALID // GPIO46, + P8_17 gpio.PinIO = gpio.INVALID // GPIO27, + P8_18 gpio.PinIO = gpio.INVALID // GPIO65, + P8_19 gpio.PinIO = gpio.INVALID // GPIO22, EHRPWM2A + P8_20 gpio.PinIO = gpio.INVALID // GPIO63, MMC1_CMD + P8_21 gpio.PinIO = gpio.INVALID // GPIO62, MMC1_CLK + P8_22 gpio.PinIO = gpio.INVALID // GPIO37, MMC1_DAT5 + P8_23 gpio.PinIO = gpio.INVALID // GPIO36, MMC1_DAT4 + P8_24 gpio.PinIO = gpio.INVALID // GPIO33, MMC1_DAT1 + P8_25 gpio.PinIO = gpio.INVALID // GPIO32, MMC1_DAT0 + P8_26 gpio.PinIO = gpio.INVALID // GPIO61, + P8_27 gpio.PinIO = gpio.INVALID // GPIO86, LCD_VSYNC + P8_28 gpio.PinIO = gpio.INVALID // GPIO88, LCD_PCLK + P8_29 gpio.PinIO = gpio.INVALID // GPIO87, LCD_HSYNC + P8_30 gpio.PinIO = gpio.INVALID // GPIO89, LCD_AC_BIAS_E + P8_31 gpio.PinIO = gpio.INVALID // GPIO10, LCD_DATA14, UART4_CTS + P8_32 gpio.PinIO = gpio.INVALID // GPIO11, LCD_DATA15, UART5_RTS + P8_33 gpio.PinIO = gpio.INVALID // GPIO9, LCD_DATA13, UART4_RTS + P8_34 gpio.PinIO = gpio.INVALID // GPIO81, LCD_DATA11, EHRPWM1B, UART3_RTS + P8_35 gpio.PinIO = gpio.INVALID // GPIO8, LCD_DATA12, UART4_CTS + P8_36 gpio.PinIO = gpio.INVALID // GPIO80, LCD_DATA10, EHRPWM1A, UART3_CTS + P8_37 gpio.PinIO = gpio.INVALID // GPIO78, LCD_DATA8, UART5_TX + P8_38 gpio.PinIO = gpio.INVALID // GPIO79, LCD_DATA9, UART5_RX + P8_39 gpio.PinIO = gpio.INVALID // GPIO76, LCD_DATA6 + P8_40 gpio.PinIO = gpio.INVALID // GPIO77, LCD_DATA7 + P8_41 gpio.PinIO = gpio.INVALID // GPIO74, LCD_DATA4 + P8_42 gpio.PinIO = gpio.INVALID // GPIO75, LCD_DATA5 + P8_43 gpio.PinIO = gpio.INVALID // GPIO72, LCD_DATA2 + P8_44 gpio.PinIO = gpio.INVALID // GPIO73, LCD_DATA3 + P8_45 gpio.PinIO = gpio.INVALID // GPIO70, LCD_DATA0, EHRPWM2A + P8_46 gpio.PinIO = gpio.INVALID // GPIO71, LCD_DATA1, EHRPWM2B + + P9_1 pin.Pin = pin.GROUND + P9_2 pin.Pin = pin.GROUND + P9_3 pin.Pin = pin.V3_3 + P9_4 pin.Pin = pin.V3_3 + P9_5 pin.Pin = pin.V5 + P9_6 pin.Pin = pin.V5 + P9_7 pin.Pin = pin.V5 + P9_8 pin.Pin = pin.V5 + P9_9 pin.Pin = PWR_BUT // PWR_BUT + P9_10 pin.Pin = RESET_OUT // SYS_RESETn + P9_11 gpio.PinIO = gpio.INVALID // GPIO30, UART4_RX + P9_12 gpio.PinIO = gpio.INVALID // GPIO60 + P9_13 gpio.PinIO = gpio.INVALID // GPIO31, UART4_TX + P9_14 gpio.PinIO = gpio.INVALID // GPIO50, EHRPWM1A + P9_15 gpio.PinIO = gpio.INVALID // GPIO48 + P9_16 gpio.PinIO = gpio.INVALID // GPIO51, EHRPWM1B + P9_17 gpio.PinIO = gpio.INVALID // GPIO5, I2C1_SCL, SPI0_CS0 + P9_18 gpio.PinIO = gpio.INVALID // GPIO4, I2C1_SDA, SPI0_MISO + P9_19 gpio.PinIO = gpio.INVALID // GPIO13, I2C2_SCL, UART1_RTS, SPI1_CS1 + P9_20 gpio.PinIO = gpio.INVALID // GPIO12, I2C2_SDA, UART1_CTS, SPI1_CS0 + P9_21 gpio.PinIO = gpio.INVALID // GPIO3, EHRPWM0B, I2C2_SCL, UART2_TX, SPI0_MOSI + P9_22 gpio.PinIO = gpio.INVALID // GPIO2, EHRPWM0A, I2C2_SDA, UART2_RX, SPI0_CLK + P9_23 gpio.PinIO = gpio.INVALID // GPIO49 + P9_24 gpio.PinIO = gpio.INVALID // GPIO15, I2C1_SCL, UART1_TX + P9_25 gpio.PinIO = gpio.INVALID // GPIO117 + P9_26 gpio.PinIO = gpio.INVALID // GPIO14, I2C1_SDA, UART1_RX + P9_27 gpio.PinIO = gpio.INVALID // GPIO115 + P9_28 gpio.PinIO = gpio.INVALID // GPIO113, ECAPPWM2, SPI1_CS0 + P9_29 gpio.PinIO = gpio.INVALID // GPIO111, EHRPWM0B, SPI1_MOSI + P9_30 gpio.PinIO = gpio.INVALID // GPIO112, SPI1_MISO + P9_31 gpio.PinIO = gpio.INVALID // GPIO110, EHRPWM0A, SPI1_CLK + P9_32 pin.Pin = VADC // VDD_ADC + P9_33 pin.Pin = AIN4 // AIN4 + P9_34 pin.Pin = AGND // GNDA_ADC + P9_35 pin.Pin = AIN6 // AIN6 + P9_36 pin.Pin = AIN5 // AIN5 + P9_37 pin.Pin = AIN2 // AIN2 + P9_38 pin.Pin = AIN3 // AIN3 + P9_39 pin.Pin = AIN0 // AIN0 + P9_40 pin.Pin = AIN1 // AIN1 + P9_41 gpio.PinIO = gpio.INVALID // GPIO20 + P9_42 gpio.PinIO = gpio.INVALID // GPIO7, ECAPPWM0, UART3_TX, SPI1_CS1 + P9_43 pin.Pin = pin.GROUND + P9_44 pin.Pin = pin.GROUND + P9_45 pin.Pin = pin.GROUND + P9_46 pin.Pin = pin.GROUND +) + +// Present returns true if the host is a BeagleBone Black/Green or their +// Wireless version. +func Present() bool { + return black.Present() || green.Present() +} + +// driver implements periph.Driver. +type driver struct { +} + +func (d *driver) String() string { + return "beaglebone" +} + +func (d *driver) Prerequisites() []string { + return []string{"am335x"} +} + +func (d *driver) After() []string { + return nil +} + +func (d *driver) Init() (bool, error) { + if !Present() { + return false, errors.New("BeagleBone board not detected") + } + + J1_4 = sysfs.Pins[42] + J1_5 = sysfs.Pins[43] + + P8_3 = sysfs.Pins[38] + P8_4 = sysfs.Pins[39] + P8_5 = sysfs.Pins[34] + P8_6 = sysfs.Pins[35] + P8_7 = sysfs.Pins[66] + P8_8 = sysfs.Pins[67] + P8_9 = sysfs.Pins[69] + P8_10 = sysfs.Pins[68] + P8_11 = sysfs.Pins[45] + P8_12 = sysfs.Pins[44] + P8_13 = sysfs.Pins[23] + P8_14 = sysfs.Pins[26] + P8_15 = sysfs.Pins[47] + P8_16 = sysfs.Pins[46] + P8_17 = sysfs.Pins[27] + P8_18 = sysfs.Pins[65] + P8_19 = sysfs.Pins[22] + P8_20 = sysfs.Pins[63] + P8_21 = sysfs.Pins[62] + P8_22 = sysfs.Pins[37] + P8_23 = sysfs.Pins[36] + P8_24 = sysfs.Pins[33] + P8_25 = sysfs.Pins[32] + P8_26 = sysfs.Pins[61] + P8_27 = sysfs.Pins[86] + P8_28 = sysfs.Pins[88] + P8_29 = sysfs.Pins[87] + P8_30 = sysfs.Pins[89] + P8_31 = sysfs.Pins[10] + P8_32 = sysfs.Pins[11] + P8_33 = sysfs.Pins[9] + P8_34 = sysfs.Pins[81] + P8_35 = sysfs.Pins[8] + P8_36 = sysfs.Pins[80] + P8_37 = sysfs.Pins[78] + P8_38 = sysfs.Pins[79] + P8_39 = sysfs.Pins[76] + P8_40 = sysfs.Pins[77] + P8_41 = sysfs.Pins[74] + P8_42 = sysfs.Pins[75] + P8_43 = sysfs.Pins[72] + P8_44 = sysfs.Pins[73] + P8_45 = sysfs.Pins[70] + P8_46 = sysfs.Pins[71] + + P9_11 = sysfs.Pins[30] + P9_12 = sysfs.Pins[60] + P9_13 = sysfs.Pins[31] + P9_14 = sysfs.Pins[50] + P9_15 = sysfs.Pins[48] + P9_16 = sysfs.Pins[51] + P9_17 = sysfs.Pins[5] + P9_18 = sysfs.Pins[4] + P9_19 = sysfs.Pins[13] + P9_20 = sysfs.Pins[12] + P9_21 = sysfs.Pins[3] + P9_22 = sysfs.Pins[2] + P9_23 = sysfs.Pins[49] + P9_24 = sysfs.Pins[15] + P9_25 = sysfs.Pins[117] + P9_26 = sysfs.Pins[14] + P9_27 = sysfs.Pins[115] + P9_28 = sysfs.Pins[113] + P9_29 = sysfs.Pins[111] + P9_30 = sysfs.Pins[112] + P9_31 = sysfs.Pins[110] + P9_41 = sysfs.Pins[20] + P9_42 = sysfs.Pins[7] + + hdr := [][]pin.Pin{{J1_1}, {J1_2}, {J1_3}, {J1_4}, {J1_5}, {J1_6}} + if err := pinreg.Register("J1", hdr); err != nil { + return true, err + } + + hdr = [][]pin.Pin{ + {P8_1, P8_2}, + {P8_3, P8_4}, + {P8_5, P8_6}, + {P8_7, P8_8}, + {P8_9, P8_10}, + {P8_11, P8_12}, + {P8_13, P8_14}, + {P8_15, P8_16}, + {P8_17, P8_18}, + {P8_19, P8_20}, + {P8_21, P8_22}, + {P8_23, P8_24}, + {P8_25, P8_26}, + {P8_27, P8_28}, + {P8_29, P8_30}, + {P8_31, P8_32}, + {P8_33, P8_34}, + {P8_35, P8_36}, + {P8_37, P8_38}, + {P8_39, P8_40}, + {P8_41, P8_42}, + {P8_43, P8_44}, + {P8_45, P8_46}, + } + if err := pinreg.Register("P8", hdr); err != nil { + return true, err + } + + hdr = [][]pin.Pin{ + {P9_1, P9_2}, + {P9_3, P9_4}, + {P9_5, P9_6}, + {P9_7, P9_8}, + {P9_9, P9_10}, + {P9_11, P9_12}, + {P9_13, P9_14}, + {P9_15, P9_16}, + {P9_17, P9_18}, + {P9_19, P9_20}, + {P9_21, P9_22}, + {P9_23, P9_24}, + {P9_25, P9_26}, + {P9_27, P9_28}, + {P9_29, P9_30}, + {P9_31, P9_32}, + {P9_33, P9_34}, + {P9_35, P9_36}, + {P9_37, P9_38}, + {P9_39, P9_40}, + {P9_41, P9_42}, + {P9_43, P9_44}, + {P9_45, P9_46}, + } + err := pinreg.Register("P9", hdr) + return true, err +} + +func init() { + if isArm { + periph.MustRegister(&drv) + } +} + +var drv driver diff --git a/vendor/periph.io/x/periph/host/beagle/bone/bone_arm.go b/vendor/periph.io/x/periph/host/beagle/bone/bone_arm.go new file mode 100644 index 0000000..aa2c07a --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/bone/bone_arm.go @@ -0,0 +1,7 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package bone + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/beagle/bone/bone_other.go b/vendor/periph.io/x/periph/host/beagle/bone/bone_other.go new file mode 100644 index 0000000..199b584 --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/bone/bone_other.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm + +package bone + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/beagle/green/green.go b/vendor/periph.io/x/periph/host/beagle/green/green.go new file mode 100644 index 0000000..7d25d3a --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/green/green.go @@ -0,0 +1,95 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package green implements headers for the BeagleBone Green and BeagleBone +// Green Wireless micro-computers. +// +// Reference +// +// https://beagleboard.org/green +// +// https://beagleboard.org/green-wireless +// +// Datasheet +// +// http://wiki.seeedstudio.com/BeagleBone_Green/ +package green + +import ( + "errors" + "strings" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/conn/pin/pinreg" + "periph.io/x/periph/host/distro" + "periph.io/x/periph/host/sysfs" +) + +// Headers found on BeagleBone Green. +var ( + // I2C Groove port. + I2C_SCL gpio.PinIO = gpio.INVALID // GPIO13, I2C2_SCL, UART1_RTS, SPI1_CS1 + I2C_SDA gpio.PinIO = gpio.INVALID // GPIO12, I2C2_SDA, UART1_CTS, SPI1_CS0 + + // UART Groove port connected to UART2. + UART_TX gpio.PinIO = gpio.INVALID // GPIO3, EHRPWM0B, I2C2_SCL, UART2_TX, SPI0_MISO + UART_RX gpio.PinIO = gpio.INVALID // GPIO2, EHRPWM0A, I2C2_SDA, UART2_RX, SPI0_CLK +) + +// Present returns true if the host is a BeagleBone Green or BeagleBone Green +// Wireless. +func Present() bool { + if isArm { + return strings.HasPrefix(distro.DTModel(), "TI AM335x BeagleBone Green") + } + return false +} + +// driver implements periph.Driver. +type driver struct { +} + +func (d *driver) String() string { + return "beaglebone-green" +} + +func (d *driver) Prerequisites() []string { + return []string{"am335x"} +} + +func (d *driver) After() []string { + return nil +} + +func (d *driver) Init() (bool, error) { + if !Present() { + return false, errors.New("BeagleBone Green board not detected") + } + + I2C_SDA = sysfs.Pins[12] + I2C_SCL = sysfs.Pins[13] + hdr := [][]pin.Pin{{pin.GROUND}, {pin.V3_3}, {I2C_SDA}, {I2C_SCL}} + if err := pinreg.Register("I2C", hdr); err != nil { + return true, err + } + + UART_TX = sysfs.Pins[3] + UART_RX = sysfs.Pins[2] + hdr = [][]pin.Pin{{pin.GROUND}, {pin.V3_3}, {UART_TX}, {UART_RX}} + if err := pinreg.Register("UART", hdr); err != nil { + return true, err + } + + return true, nil +} + +func init() { + if isArm { + periph.MustRegister(&drv) + } +} + +var drv driver diff --git a/vendor/periph.io/x/periph/host/beagle/green/green_arm.go b/vendor/periph.io/x/periph/host/beagle/green/green_arm.go new file mode 100644 index 0000000..cc48c29 --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/green/green_arm.go @@ -0,0 +1,7 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package green + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/beagle/green/green_other.go b/vendor/periph.io/x/periph/host/beagle/green/green_other.go new file mode 100644 index 0000000..72de28f --- /dev/null +++ b/vendor/periph.io/x/periph/host/beagle/green/green_other.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm + +package green + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/chip/chip.go b/vendor/periph.io/x/periph/host/chip/chip.go new file mode 100644 index 0000000..19cd016 --- /dev/null +++ b/vendor/periph.io/x/periph/host/chip/chip.go @@ -0,0 +1,358 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package chip + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/conn/pin/pinreg" + "periph.io/x/periph/host/allwinner" + "periph.io/x/periph/host/distro" + "periph.io/x/periph/host/fs" +) + +// C.H.I.P. hardware pins. +var ( + TEMP_SENSOR = &pin.BasicPin{N: "TEMP_SENSOR"} + PWR_SWITCH = &pin.BasicPin{N: "PWR_SWITCH"} + // XIO "gpio" pins attached to the pcf8574 I²C port extender. + XIO0, XIO1, XIO2, XIO3, XIO4, XIO5, XIO6, XIO7 gpio.PinIO +) + +// The U13 header is opposite the power LED. +// +// The alternate pin functionality is described at pages 322-323 of +// https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20User%20Manual%20V1.1.pdf +var ( + U13_1 = pin.GROUND // + U13_2 = pin.DC_IN // + U13_3 = pin.V5 // (filtered) + U13_4 = pin.GROUND // + U13_5 = pin.V3_3 // + U13_6 = TEMP_SENSOR // Analog temp sensor input + U13_7 = pin.V1_8 // + U13_8 = pin.BAT_PLUS // External LiPo battery + U13_9 = allwinner.PB16 // I2C1_SDA + U13_10 = PWR_SWITCH // Power button + U13_11 = allwinner.PB15 // I2C1_SCL + U13_12 = pin.GROUND // + U13_13 = allwinner.X1 // Touch screen X1 + U13_14 = allwinner.X2 // Touch screen X2 + U13_15 = allwinner.Y1 // Touch screen Y1 + U13_16 = allwinner.Y2 // Touch screen Y2 + U13_17 = allwinner.PD2 // LCD-D2; UART2_TX firmware probe for 1-wire to detect DIP at boot; http://docs.getchip.com/dip.html#dip-identification + U13_18 = allwinner.PB2 // PWM0; EINT16 + U13_19 = allwinner.PD4 // LCD-D4; UART2_CTS + U13_20 = allwinner.PD3 // LCD-D3; UART2_RX + U13_21 = allwinner.PD6 // LCD-D6 + U13_22 = allwinner.PD5 // LCD-D5 + U13_23 = allwinner.PD10 // LCD-D10 + U13_24 = allwinner.PD7 // LCD-D7 + U13_25 = allwinner.PD12 // LCD-D12 + U13_26 = allwinner.PD11 // LCD-D11 + U13_27 = allwinner.PD14 // LCD-D14 + U13_28 = allwinner.PD13 // LCD-D13 + U13_29 = allwinner.PD18 // LCD-D18 + U13_30 = allwinner.PD15 // LCD-D15 + U13_31 = allwinner.PD20 // LCD-D20 + U13_32 = allwinner.PD19 // LCD-D19 + U13_33 = allwinner.PD22 // LCD-D22 + U13_34 = allwinner.PD21 // LCD-D21 + U13_35 = allwinner.PD24 // LCD-CLK + U13_36 = allwinner.PD23 // LCD-D23 + U13_37 = allwinner.PD26 // LCD-VSYNC + U13_38 = allwinner.PD27 // LCD-HSYNC + U13_39 = pin.GROUND // + U13_40 = allwinner.PD25 // LCD-DE: RGB666 data +) + +// The U14 header is right next to the power LED. +var ( + U14_1 = pin.GROUND // + U14_2 = pin.V5 // (filtered) + U14_3 = allwinner.PG3 // UART1_TX; EINT3 + U14_4 = allwinner.HP_LEFT // Headphone left output + U14_5 = allwinner.PG4 // UART1_RX; EINT4 + U14_6 = allwinner.HP_COM // Headphone amp out + U14_7 = allwinner.FEL // Boot mode selection + U14_8 = allwinner.HP_RIGHT // Headphone right output + U14_9 = pin.V3_3 // + U14_10 = allwinner.MIC_GND // Microphone ground + U14_11 = allwinner.KEY_ADC // LRADC Low res analog to digital + U14_12 = allwinner.MIC_IN // Microphone input + U14_13 = XIO0 // gpio via I²C controller + U14_14 = XIO1 // gpio via I²C controller + U14_15 = XIO2 // gpio via I²C controller + U14_16 = XIO3 // gpio via I²C controller + U14_17 = XIO4 // gpio via I²C controller + U14_18 = XIO5 // gpio via I²C controller + U14_19 = XIO6 // gpio via I²C controller + U14_20 = XIO7 // gpio via I²C controller + U14_21 = pin.GROUND // + U14_22 = pin.GROUND // + U14_23 = allwinner.PG1 // GPS_CLK; AP-EINT1 + U14_24 = allwinner.PB3 // IR_TX; AP-EINT3 (EINT17) + U14_25 = allwinner.PB18 // I2C2_SDA + U14_26 = allwinner.PB17 // I2C2_SCL + U14_27 = allwinner.PE0 // CSIPCK: CMOS serial interface; SPI2_CS0; EINT14 + U14_28 = allwinner.PE1 // CSICK: CMOS serial interface; SPI2_CLK; EINT15 + U14_29 = allwinner.PE2 // CSIHSYNC; SPI2_MOSI + U14_30 = allwinner.PE3 // CSIVSYNC; SPI2_MISO + U14_31 = allwinner.PE4 // CSID0 + U14_32 = allwinner.PE5 // CSID1 + U14_33 = allwinner.PE6 // CSID2 + U14_34 = allwinner.PE7 // CSID3 + U14_35 = allwinner.PE8 // CSID4 + U14_36 = allwinner.PE9 // CSID5 + U14_37 = allwinner.PE10 // CSID6; UART1_RX + U14_38 = allwinner.PE11 // CSID7; UART1_TX + U14_39 = pin.GROUND // + U14_40 = pin.GROUND // +) + +// Present returns true if running on a NextThing Co's C.H.I.P. board. +// +// It looks for "C.H.I.P" in the device tree. The following information is +// expected in the device dtree: +// root@chip2:/proc/device-tree# od -c compatible +// 0000000 n e x t t h i n g , c h i p \0 a +// 0000020 l l w i n n e r , s u n 5 i - r +// 0000040 8 \0 +// root@chip2:/proc/device-tree# od -c model +// 0000000 N e x t T h i n g C . H . I . +// 0000020 P . \0 +func Present() bool { + return strings.Contains(distro.DTModel(), "C.H.I.P") +} + +// + +// aliases is a list of aliases for the various gpio pins, this allows users to +// refer to pins using the documented and labeled names instead of some GPIOnnn +// name. The map key is the alias and the value is the real pin name. +var aliases = map[string]string{ + "AP-EINT1": "PG1", + "AP-EINT3": "PB3", + "CSIPCK": "PE0", + "CSIHSYNC": "PE2", + "CSID0": "PE4", + "CSID2": "PE6", + "CSID4": "PE8", + "CSID6": "PE10", + "CSICK": "PE1", + "CSIVSYNC": "PE3", + "CSID1": "PE5", + "CSID3": "PE7", + "CSID5": "PE9", + "CSID7": "PE11", + "LCD-CLK": "PD24", + "LCD-D10": "PD10", + "LCD-D11": "PD11", + "LCD-D12": "PD12", + "LCD-D13": "PD13", + "LCD-D14": "PD14", + "LCD-D15": "PD15", + "LCD-D18": "PD18", + "LCD-D19": "PD19", + "LCD-D2": "PD2", + "LCD-D20": "PD20", + "LCD-D21": "PD21", + "LCD-D22": "PD22", + "LCD-D23": "PD23", + "LCD-D3": "PD3", + "LCD-D4": "PD4", + "LCD-D5": "PD5", + "LCD-D6": "PD6", + "LCD-D7": "PD7", + "LCD-DE": "PD25", + "LCD-HSYNC": "PD27", + "LCD-VSYNC": "PD26", + "TWI1-SCK": "PB15", + "TWI1-SDA": "PB16", + "TWI2-SCK": "PB17", + "TWI2-SDA": "PB18", + "UART1-RX": "PG4", + "UART1-TX": "PG3", +} + +func init() { + // These are initialized later by the driver. + XIO0 = gpio.INVALID + XIO1 = gpio.INVALID + XIO2 = gpio.INVALID + XIO3 = gpio.INVALID + XIO4 = gpio.INVALID + XIO5 = gpio.INVALID + XIO6 = gpio.INVALID + XIO7 = gpio.INVALID + // These must be reinitialized. + U14_13 = XIO0 + U14_14 = XIO1 + U14_15 = XIO2 + U14_16 = XIO3 + U14_17 = XIO4 + U14_18 = XIO5 + U14_19 = XIO6 + U14_20 = XIO7 +} + +// findXIOBase calculates the base of the XIO-P? gpio pins as explained in +// http://docs.getchip.com/chip.html#kernel-4-3-vs-4-4-gpio-how-to-tell-the-difference +// +// The XIO-P? sysfs mapped pin number changed in kernel 4.3, 4.4.11 and again +// in 4.4.13 so it is better to query sysfs. +func findXIOBase() int { + chips, err := filepath.Glob("/sys/class/gpio/gpiochip*/label") + if err != nil { + return -1 + } + for _, item := range chips { + f, err := fs.Open(item, os.O_RDONLY) + if err != nil { + continue + } + b, err := ioutil.ReadAll(f) + if err1 := f.Close(); err == nil { + err = err1 + } + if err != nil { + continue + } + if string(b) == "pcf8574a\n" { + id, err := strconv.Atoi(filepath.Base(filepath.Dir(item))[8:]) + if err != nil { + return -1 + } + return id + } + } + return -1 +} + +// driver implements drivers.Driver. +type driver struct { +} + +func (d *driver) String() string { + return "chip" +} + +func (d *driver) Prerequisites() []string { + return nil +} + +func (d *driver) After() []string { + // has allwinner cpu, needs sysfs for XIO0-XIO7 "gpio" pins + return []string{"allwinner-gpio", "sysfs-gpio"} +} + +func (d *driver) Init() (bool, error) { + if !Present() { + return false, errors.New("NextThing Co. CHIP board not detected") + } + + base := findXIOBase() + if base == -1 { + return true, errors.New("couldn't find XIO pins base number") + } + for i := 0; i < 8; i++ { + aliases[fmt.Sprintf("XIO-P%d", i)] = fmt.Sprintf("GPIO%d", base+i) + } + + // At this point the sysfs driver has initialized and discovered its pins, + // we can now hook-up the appropriate CHIP pins to sysfs gpio pins. + for alias, real := range aliases { + if err := gpioreg.RegisterAlias(alias, real); err != nil { + return true, err + } + } + // These must be explicitly initialized. + XIO0 = gpioreg.ByName("XIO-P0") + XIO1 = gpioreg.ByName("XIO-P1") + XIO2 = gpioreg.ByName("XIO-P2") + XIO3 = gpioreg.ByName("XIO-P3") + XIO4 = gpioreg.ByName("XIO-P4") + XIO5 = gpioreg.ByName("XIO-P5") + XIO6 = gpioreg.ByName("XIO-P6") + XIO7 = gpioreg.ByName("XIO-P7") + U14_13 = XIO0 + U14_14 = XIO1 + U14_15 = XIO2 + U14_16 = XIO3 + U14_17 = XIO4 + U14_18 = XIO5 + U14_19 = XIO6 + U14_20 = XIO7 + + // U13 is one of the 20x2 connectors. + U13 := [][]pin.Pin{ + {U13_1, U13_2}, + {U13_3, U13_4}, + {U13_5, U13_6}, + {U13_7, U13_8}, + {gpioreg.ByName("TWI1-SDA"), U13_10}, + {gpioreg.ByName("TWI1-SCK"), U13_12}, + {U13_13, U13_14}, + {U13_15, U13_16}, + {gpioreg.ByName("LCD-D2"), gpioreg.ByName("PWM0")}, + {gpioreg.ByName("LCD-D4"), gpioreg.ByName("LCD-D3")}, + {gpioreg.ByName("LCD-D6"), gpioreg.ByName("LCD-D5")}, + {gpioreg.ByName("LCD-D10"), gpioreg.ByName("LCD-D7")}, + {gpioreg.ByName("LCD-D12"), gpioreg.ByName("LCD-D11")}, + {gpioreg.ByName("LCD-D14"), gpioreg.ByName("LCD-D13")}, + {gpioreg.ByName("LCD-D18"), gpioreg.ByName("LCD-D15")}, + {gpioreg.ByName("LCD-D20"), gpioreg.ByName("LCD-D19")}, + {gpioreg.ByName("LCD-D22"), gpioreg.ByName("LCD-D21")}, + {gpioreg.ByName("LCD-CLK"), gpioreg.ByName("LCD-D23")}, + {gpioreg.ByName("LCD-VSYNC"), gpioreg.ByName("LCD-HSYNC")}, + {U13_39, gpioreg.ByName("LCD-DE")}, + } + if err := pinreg.Register("U13", U13); err != nil { + return true, err + } + + // U14 is one of the 20x2 connectors. + U14 := [][]pin.Pin{ + {U14_1, U14_2}, + {gpioreg.ByName("UART1-TX"), U14_4}, + {gpioreg.ByName("UART1-RX"), U14_6}, + {U14_7, U14_8}, + {U14_9, U14_10}, + {U14_11, U14_12}, // TODO(maruel): switch to LRADC once analog support is added + {U14_13, U14_14}, + {U14_15, U14_16}, + {U14_17, U14_18}, + {U14_19, U14_20}, + {U14_21, U14_22}, + {gpioreg.ByName("AP-EINT1"), gpioreg.ByName("AP-EINT3")}, + {gpioreg.ByName("TWI2-SDA"), gpioreg.ByName("TWI2-SCK")}, + {gpioreg.ByName("CSIPCK"), gpioreg.ByName("CSICK")}, + {gpioreg.ByName("CSIHSYNC"), gpioreg.ByName("CSIVSYNC")}, + {gpioreg.ByName("CSID0"), gpioreg.ByName("CSID1")}, + {gpioreg.ByName("CSID2"), gpioreg.ByName("CSID3")}, + {gpioreg.ByName("CSID4"), gpioreg.ByName("CSID5")}, + {gpioreg.ByName("CSID6"), gpioreg.ByName("CSID7")}, + {U14_39, U14_40}, + } + return true, pinreg.Register("U14", U14) +} + +func init() { + if isArm { + periph.MustRegister(&drv) + } +} + +var drv driver diff --git a/vendor/periph.io/x/periph/host/chip/chip_arm.go b/vendor/periph.io/x/periph/host/chip/chip_arm.go new file mode 100644 index 0000000..4b89be0 --- /dev/null +++ b/vendor/periph.io/x/periph/host/chip/chip_arm.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package chip + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/chip/chip_other.go b/vendor/periph.io/x/periph/host/chip/chip_other.go new file mode 100644 index 0000000..2cf74a3 --- /dev/null +++ b/vendor/periph.io/x/periph/host/chip/chip_other.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm + +package chip + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/chip/doc.go b/vendor/periph.io/x/periph/host/chip/doc.go new file mode 100644 index 0000000..c55d43b --- /dev/null +++ b/vendor/periph.io/x/periph/host/chip/doc.go @@ -0,0 +1,30 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package chip contains header definitions for NextThing Co's C.H.I.P. board. +// +// CHIP uses the Allwinner R8 processor and thus the allwinner host package is +// automatically imported. +// +// This package exports the U13 header, which is opposite the power LED, and +// U14, which is right next to the power LED. Most of the pins are usable as +// GPIO and are directly to the processor. These can use memory-mapped GPIO, +// which is very fast. The XIO-P0 through XIO-P7 pins are attached to a pcf8574 +// I²C expander which has the result that all accesses to these pins have to go +// through the kernel and the I²C bus protocol, i.e., they're slow. +// +// GPIO edge detection (using interrupts) is only supported on a few of the +// processor's pins: AP-EINT1, AP-EINT3, CSIPCK, and CSICK. Edge detection is +// also supported on the XIO pins, but this feature is rather limited due to +// the device and the driver (for example, the driver interrupts on all edges). +// +// References +// +// http://www.chip-community.org/index.php/Hardware_Information +// +// http://docs.getchip.com/chip.html#chip-hardware +// +// A graphical view of the board headers is available at: +// http://docs.getchip.com/chip.html#pin-headers +package chip diff --git a/vendor/periph.io/x/periph/host/cpu/cpu.go b/vendor/periph.io/x/periph/host/cpu/cpu.go new file mode 100644 index 0000000..c4e1ffd --- /dev/null +++ b/vendor/periph.io/x/periph/host/cpu/cpu.go @@ -0,0 +1,83 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package cpu + +import ( + "io" + "io/ioutil" + "os" + "strconv" + "strings" + "sync" + "time" + + "periph.io/x/periph/host/fs" +) + +// MaxSpeed returns the processor maximum speed in Hz. +// +// Returns 0 if it couldn't be calculated. +func MaxSpeed() int64 { + if isLinux { + return getMaxSpeedLinux() + } + return 0 +} + +// Nanospin spins for a short amount of time doing a busy loop. +// +// This function should be called with durations of 10µs or less. +func Nanospin(d time.Duration) { + // TODO(maruel): Use runtime.LockOSThread()? + if isLinux { + nanospinLinux(d) + } else { + nanospinTime(d) + } +} + +// + +var ( + mu sync.Mutex + maxSpeed int64 = -1 + openFile = openFileOrig +) + +func openFileOrig(path string, flag int) (io.ReadCloser, error) { + f, err := fs.Open(path, flag) + if err != nil { + return nil, err + } + return f, nil +} + +func getMaxSpeedLinux() int64 { + mu.Lock() + defer mu.Unlock() + if maxSpeed == -1 { + maxSpeed = 0 + if f, err := openFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", os.O_RDONLY); err == nil { + defer f.Close() + if b, err := ioutil.ReadAll(f); err == nil { + s := strings.TrimSpace(string(b)) + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + // Weirdly, the speed is listed as khz. :( + maxSpeed = i * 1000 + } + } + } + } + return maxSpeed +} + +func nanospinTime(d time.Duration) { + // TODO(maruel): That's not optimal; it's actually pretty bad. + // time.Sleep() sleeps for really too long, calling it repeatedly with + // minimal value will give the caller a wake rate of 5KHz or so, depending on + // the host. This makes it useless for bitbanging protocol implementations. + for start := time.Now(); time.Since(start) < d; { + } +} diff --git a/vendor/periph.io/x/periph/host/cpu/cpu_linux.go b/vendor/periph.io/x/periph/host/cpu/cpu_linux.go new file mode 100644 index 0000000..c227c88 --- /dev/null +++ b/vendor/periph.io/x/periph/host/cpu/cpu_linux.go @@ -0,0 +1,22 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package cpu + +import ( + "syscall" + "time" +) + +const isLinux = true + +func nanospinLinux(d time.Duration) { + // runtime.nanotime() is not exported so it cannot be used to busy loop for + // very short sleep (10µs or less). + time := syscall.NsecToTimespec(d.Nanoseconds()) + leftover := syscall.Timespec{} + for syscall.Nanosleep(&time, &leftover) != nil { + time = leftover + } +} diff --git a/vendor/periph.io/x/periph/host/cpu/cpu_other.go b/vendor/periph.io/x/periph/host/cpu/cpu_other.go new file mode 100644 index 0000000..a473633 --- /dev/null +++ b/vendor/periph.io/x/periph/host/cpu/cpu_other.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !linux + +package cpu + +import "time" + +const isLinux = false + +func nanospinLinux(d time.Duration) { +} diff --git a/vendor/periph.io/x/periph/host/cpu/doc.go b/vendor/periph.io/x/periph/host/cpu/doc.go new file mode 100644 index 0000000..34cd4de --- /dev/null +++ b/vendor/periph.io/x/periph/host/cpu/doc.go @@ -0,0 +1,6 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package cpu implements functions relating to the host CPU itself. +package cpu diff --git a/vendor/periph.io/x/periph/host/distro/devtree.go b/vendor/periph.io/x/periph/host/distro/devtree.go new file mode 100644 index 0000000..d31eb70 --- /dev/null +++ b/vendor/periph.io/x/periph/host/distro/devtree.go @@ -0,0 +1,61 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package distro + +// DTModel returns platform model info from the Linux device tree (/proc/device-tree/model), and +// returns "unknown" on non-linux systems or if the file is missing. +func DTModel() string { + mu.Lock() + defer mu.Unlock() + + if dtModel == "" { + dtModel = "" + if isLinux { + dtModel = makeDTModelLinux() + } + } + return dtModel +} + +// DTCompatible returns platform compatibility info from the Linux device tree +// (/proc/device-tree/compatible), and returns []{"unknown"} on non-linux systems or if the file is +// missing. +func DTCompatible() []string { + mu.Lock() + defer mu.Unlock() + + if dtCompatible == nil { + dtCompatible = []string{} + if isLinux { + dtCompatible = makeDTCompatible() + } + } + return dtCompatible +} + +// + +var ( + dtModel string // cached /proc/device-tree/model + dtCompatible []string // cached /proc/device-tree/compatible +) + +func makeDTModelLinux() string { + // Read model from device tree. + if bytes, err := readFile("/proc/device-tree/model"); err == nil { + if model := splitNull(bytes); len(model) > 0 { + return model[0] + } + } + return "" +} + +func makeDTCompatible() []string { + // Read compatible from device tree. + if bytes, err := readFile("/proc/device-tree/compatible"); err == nil { + return splitNull(bytes) + } + return []string{} +} diff --git a/vendor/periph.io/x/periph/host/distro/distro.go b/vendor/periph.io/x/periph/host/distro/distro.go new file mode 100644 index 0000000..96e49df --- /dev/null +++ b/vendor/periph.io/x/periph/host/distro/distro.go @@ -0,0 +1,189 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package distro implements common functionality to auto-detect features on +// the host; generally about linux distributions. +// +// Most of the functions exported as in the form IsFoo() where Foo is a linux +// distribution. +package distro + +import ( + "io/ioutil" + "os" + "strconv" + "strings" + "sync" + "unicode" +) + +// IsArmbian returns true if running on a Armbian distribution. +// +// http://www.armbian.com/ +func IsArmbian() bool { + if isArm && isLinux { + // Armbian presents itself as debian in /etc/os-release so OSRelease() + // cannot be used.. + _, err := os.Stat("/etc/armbian.txt") + return err == nil + } + return false +} + +// IsDebian returns true if running on an Debian derived distribution. +// +// This function returns true on both Armbian, Raspbian and Ubuntu. +// +// https://debian.org/ +func IsDebian() bool { + if isLinux { + // http://0pointer.de/public/systemd-man/os-release.html#ID_LIKE= + if OSRelease()["ID"] == "debian" { + return true + } + for _, part := range strings.Split(OSRelease()["ID_LIKE"], " ") { + if part == "debian" { + return true + } + } + } + return false +} + +// IsRaspbian returns true if running on a Raspbian distribution. +// +// https://raspbian.org/ +func IsRaspbian() bool { + if isArm && isLinux { + return OSRelease()["ID"] == "raspbian" + } + return false +} + +// IsUbuntu returns true if running on an Ubuntu derived distribution. +// +// https://ubuntu.com/ +func IsUbuntu() bool { + if isLinux { + return OSRelease()["ID"] == "ubuntu" + } + return false +} + +// OSRelease returns parsed data from /etc/os-release. +// +// For more information, see +// http://0pointer.de/public/systemd-man/os-release.html +func OSRelease() map[string]string { + if isLinux { + return makeOSReleaseLinux() + } + return osRelease +} + +// CPU + +// CPUInfo returns parsed data from /proc/cpuinfo. +func CPUInfo() map[string]string { + if isLinux { + return makeCPUInfoLinux() + } + return cpuInfo +} + +// + +var ( + mu sync.Mutex + cpuInfo map[string]string + osRelease map[string]string + readFile = ioutil.ReadFile +) + +func splitSemiColon(content string) map[string]string { + // Strictly speaking this format isn't ok, there can be multiple group. + out := map[string]string{} + for _, line := range strings.Split(content, "\n") { + parts := strings.SplitN(line, ":", 2) + if len(parts) != 2 { + continue + } + // This format may have space around the ':'. + key := strings.TrimRightFunc(parts[0], unicode.IsSpace) + if len(key) == 0 || key[0] == '#' { + continue + } + // Ignore duplicate keys. + // TODO(maruel): Keep them all. + if _, ok := out[key]; !ok { + // Trim on both side, trailing space was observed on "Features" value. + out[key] = strings.TrimFunc(parts[1], unicode.IsSpace) + } + } + return out +} + +func splitStrict(content string) map[string]string { + out := map[string]string{} + for _, line := range strings.Split(content, "\n") { + parts := strings.SplitN(line, "=", 2) + if len(parts) != 2 { + continue + } + key := parts[0] + if len(key) == 0 || key[0] == '#' { + continue + } + // Overwrite previous key. + value := parts[1] + if len(value) > 2 && value[0] == '"' && value[len(value)-1] == '"' { + // Not exactly 100% right but #closeenough. See for more details + // https://www.freedesktop.org/software/systemd/man/os-release.html + var err error + value, err = strconv.Unquote(value) + if err != nil { + continue + } + } + out[key] = value + } + return out +} + +// splitNull returns the null-terminated strings in the data +func splitNull(data []byte) []string { + ss := strings.Split(string(data), "\x00") + // The last string is typically null-terminated, so remove empty string + // from end of array. + if len(ss) > 0 && len(ss[len(ss)-1]) == 0 { + ss = ss[:len(ss)-1] + } + return ss +} + +func makeCPUInfoLinux() map[string]string { + mu.Lock() + defer mu.Unlock() + if cpuInfo == nil { + cpuInfo = map[string]string{} + if bytes, err := readFile("/proc/cpuinfo"); err == nil { + cpuInfo = splitSemiColon(string(bytes)) + } + } + return cpuInfo +} + +func makeOSReleaseLinux() map[string]string { + mu.Lock() + defer mu.Unlock() + if osRelease == nil { + // This file may not exist on older distros. Send a PR if you want to have + // a specific fallback. + osRelease = map[string]string{} + if bytes, err := readFile("/etc/os-release"); err == nil { + osRelease = splitStrict(string(bytes)) + } + } + return osRelease +} diff --git a/vendor/periph.io/x/periph/host/distro/distro_arm.go b/vendor/periph.io/x/periph/host/distro/distro_arm.go new file mode 100644 index 0000000..90a2335 --- /dev/null +++ b/vendor/periph.io/x/periph/host/distro/distro_arm.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package distro + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/distro/distro_arm64.go b/vendor/periph.io/x/periph/host/distro/distro_arm64.go new file mode 100644 index 0000000..d5734bc --- /dev/null +++ b/vendor/periph.io/x/periph/host/distro/distro_arm64.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build arm64 + +package distro + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/distro/distro_linux.go b/vendor/periph.io/x/periph/host/distro/distro_linux.go new file mode 100644 index 0000000..7679a8f --- /dev/null +++ b/vendor/periph.io/x/periph/host/distro/distro_linux.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package distro + +const isLinux = true diff --git a/vendor/periph.io/x/periph/host/distro/distro_nonarm.go b/vendor/periph.io/x/periph/host/distro/distro_nonarm.go new file mode 100644 index 0000000..075cad5 --- /dev/null +++ b/vendor/periph.io/x/periph/host/distro/distro_nonarm.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm,!arm64 + +package distro + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/distro/distro_nonlinux.go b/vendor/periph.io/x/periph/host/distro/distro_nonlinux.go new file mode 100644 index 0000000..7fd1599 --- /dev/null +++ b/vendor/periph.io/x/periph/host/distro/distro_nonlinux.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !linux + +package distro + +const isLinux = false diff --git a/vendor/periph.io/x/periph/host/doc.go b/vendor/periph.io/x/periph/host/doc.go new file mode 100644 index 0000000..ee1b9af --- /dev/null +++ b/vendor/periph.io/x/periph/host/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package host defines the host itself. +// +// The host is the machine where this code is running. +// +// Subpackages contain the drivers that are loaded automatically. +package host diff --git a/vendor/periph.io/x/periph/host/fs/fs.go b/vendor/periph.io/x/periph/host/fs/fs.go new file mode 100644 index 0000000..a85d965 --- /dev/null +++ b/vendor/periph.io/x/periph/host/fs/fs.go @@ -0,0 +1,130 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package fs provides access to the file system on the host. +// +// It exposes ioctl syscall and epoll in an OS agnostic way and permits +// completely disabling file access to lock down unit tests. +package fs + +import ( + "errors" + "os" + "sync" +) + +// Ioctler is a file handle that supports ioctl calls. +type Ioctler interface { + // Ioctl sends a linux ioctl on the file handle. op is effectively a uint32. + // + // The op is expected to be encoded in the format on x64. ARM happens to + // share the same format. + Ioctl(op uint, data uintptr) error +} + +// Open opens a file. +// +// Returns an error if Inhibit() was called. +func Open(path string, flag int) (*File, error) { + mu.Lock() + if inhibited { + mu.Unlock() + return nil, errors.New("file I/O is inhibited") + } + used = true + mu.Unlock() + + f, err := os.OpenFile(path, flag, 0600) + if err != nil { + return nil, err + } + return &File{f}, nil +} + +// Inhibit inhibits any future file I/O. It panics if any file was opened up to +// now. +// +// It should only be called in unit tests. +func Inhibit() { + mu.Lock() + inhibited = true + if used { + panic("calling Inhibit() while files were already opened") + } + mu.Unlock() +} + +// File is a superset of os.File. +type File struct { + *os.File +} + +// Ioctl sends an ioctl to the file handle. +func (f *File) Ioctl(op uint, data uintptr) error { + if isMIPS { + var err error + if op, err = translateOpMIPS(op); err != nil { + return err + } + } + return ioctl(f.Fd(), op, data) +} + +// Event is a file system event. +type Event struct { + event +} + +// MakeEvent initializes an epoll *edge* triggered event on linux. +// +// An edge triggered event is basically an "auto-reset" event, where waiting on +// the edge resets it. A level triggered event requires manual resetting; this +// could be done via a Read() call but there's no need to require the user to +// call Read(). This is particularly useless in the case of gpio.RisingEdge and +// gpio.FallingEdge. +// +// As per the official doc, edge triggers is still remembered even when no +// epoll_wait() call is running, so no edge is missed. Two edges will be +// coallesced into one if the user mode process can't keep up. There's no +// accumulation of edges. +func (e *Event) MakeEvent(fd uintptr) error { + return e.event.makeEvent(fd) +} + +// Wait waits for an event or the specified amount of time. +func (e *Event) Wait(timeoutms int) (int, error) { + return e.event.wait(timeoutms) +} + +// + +var ( + mu sync.Mutex + inhibited bool + used bool +) + +func translateOpMIPS(op uint) (uint, error) { + // Decode the arm/x64 encoding and reencode as MIPS specific linux ioctl. + // arm/x64: DIR(2), SIZE(14), TYPE(8), NR(8) + // mips: DIR(3), SIZE(13), TYPE(8), NR(8) + // Check for size overflow. + if (op & (1 << (13 + 8 + 8))) != 0 { + return 0, errors.New("fs: op code size is too large") + } + const mask = (1 << (13 + 8 + 8)) - 1 + out := op & mask + // Convert dir. + switch op >> (14 + 8 + 8) { + case 0: // none + out |= 1 << (13 + 8 + 8) + case 1: // write + out |= 4 << (13 + 8 + 8) + case 2: // read + out |= 2 << (13 + 8 + 8) + default: + return 0, errors.New("fs: op code dir is invalid") + } + return out, nil +} diff --git a/vendor/periph.io/x/periph/host/fs/fs_linux.go b/vendor/periph.io/x/periph/host/fs/fs_linux.go new file mode 100644 index 0000000..d07ed82 --- /dev/null +++ b/vendor/periph.io/x/periph/host/fs/fs_linux.go @@ -0,0 +1,57 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package fs + +import "syscall" + +const isLinux = true + +func ioctl(f uintptr, op uint, arg uintptr) error { + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f, uintptr(op), arg); errno != 0 { + return syscall.Errno(errno) + } + return nil +} + +const ( + epollET = 1 << 31 + epollPRI = 2 + epollCTLAdd = 1 + epollCTLDel = 2 + epollCTLMod = 3 +) + +type event struct { + event [1]syscall.EpollEvent + epollFd int + fd int +} + +// makeEvent creates an epoll *edge* triggered event. +// +// References: +// behavior and flags: http://man7.org/linux/man-pages/man7/epoll.7.html +// syscall.EpollCreate: http://man7.org/linux/man-pages/man2/epoll_create.2.html +// syscall.EpollCtl: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html +func (e *event) makeEvent(fd uintptr) error { + epollFd, err := syscall.EpollCreate(1) + if err != nil { + return err + } + e.epollFd = epollFd + e.fd = int(fd) + // EPOLLWAKEUP could be used to force the system to not go do sleep while + // waiting for an edge. This is generally a bad idea, as we'd instead have + // the system to *wake up* when an edge is triggered. Achieving this is + // outside the scope of this interface. + e.event[0].Events = epollPRI | epollET + e.event[0].Fd = int32(e.fd) + return syscall.EpollCtl(e.epollFd, epollCTLAdd, e.fd, &e.event[0]) +} + +func (e *event) wait(timeoutms int) (int, error) { + // http://man7.org/linux/man-pages/man2/epoll_wait.2.html + return syscall.EpollWait(e.epollFd, e.event[:], timeoutms) +} diff --git a/vendor/periph.io/x/periph/host/fs/fs_other.go b/vendor/periph.io/x/periph/host/fs/fs_other.go new file mode 100644 index 0000000..d1157d5 --- /dev/null +++ b/vendor/periph.io/x/periph/host/fs/fs_other.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !linux + +package fs + +import "errors" + +const isLinux = false + +func ioctl(f uintptr, op uint, arg uintptr) error { + return errors.New("fs: ioctl not supported on non-linux") +} + +type event struct{} + +func (e *event) makeEvent(f uintptr) error { + return errors.New("fs: unreachable code") +} + +func (e *event) wait(timeoutms int) (int, error) { + return 0, errors.New("fs: unreachable code") +} diff --git a/vendor/periph.io/x/periph/host/fs/ioctl_mips_like.go b/vendor/periph.io/x/periph/host/fs/ioctl_mips_like.go new file mode 100644 index 0000000..ed1ef57 --- /dev/null +++ b/vendor/periph.io/x/periph/host/fs/ioctl_mips_like.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build mips mipsle + +package fs + +const isMIPS = true diff --git a/vendor/periph.io/x/periph/host/fs/ioctl_other.go b/vendor/periph.io/x/periph/host/fs/ioctl_other.go new file mode 100644 index 0000000..225807a --- /dev/null +++ b/vendor/periph.io/x/periph/host/fs/ioctl_other.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !mips,!mipsle + +package fs + +const isMIPS = false diff --git a/vendor/periph.io/x/periph/host/host.go b/vendor/periph.io/x/periph/host/host.go new file mode 100644 index 0000000..64ccef9 --- /dev/null +++ b/vendor/periph.io/x/periph/host/host.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package host + +import "periph.io/x/periph" + +// Init calls periph.Init() and returns it as-is. +// +// The only difference is that by calling host.Init(), you are guaranteed to +// have all the drivers implemented in this library to be implicitly loaded. +func Init() (*periph.State, error) { + return periph.Init() +} diff --git a/vendor/periph.io/x/periph/host/host_arm.go b/vendor/periph.io/x/periph/host/host_arm.go new file mode 100644 index 0000000..3227b9a --- /dev/null +++ b/vendor/periph.io/x/periph/host/host_arm.go @@ -0,0 +1,20 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package host + +import ( + // Make sure CPU and board drivers are registered. + _ "periph.io/x/periph/host/allwinner" + _ "periph.io/x/periph/host/am335x" + _ "periph.io/x/periph/host/bcm283x" + _ "periph.io/x/periph/host/beagle/bone" + _ "periph.io/x/periph/host/beagle/green" + _ "periph.io/x/periph/host/chip" + _ "periph.io/x/periph/host/odroidc1" + // While this board is ARM64, it may run ARM 32 bits binaries so load it on + // 32 bits builds too. + _ "periph.io/x/periph/host/pine64" + _ "periph.io/x/periph/host/rpi" +) diff --git a/vendor/periph.io/x/periph/host/host_arm64.go b/vendor/periph.io/x/periph/host/host_arm64.go new file mode 100644 index 0000000..fdeb96f --- /dev/null +++ b/vendor/periph.io/x/periph/host/host_arm64.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package host + +import ( + // Make sure CPU and board drivers are registered. + _ "periph.io/x/periph/host/allwinner" + _ "periph.io/x/periph/host/bcm283x" + _ "periph.io/x/periph/host/pine64" + _ "periph.io/x/periph/host/rpi" +) diff --git a/vendor/periph.io/x/periph/host/host_linux.go b/vendor/periph.io/x/periph/host/host_linux.go new file mode 100644 index 0000000..72f93a7 --- /dev/null +++ b/vendor/periph.io/x/periph/host/host_linux.go @@ -0,0 +1,10 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package host + +import ( + // Make sure sysfs drivers are registered. + _ "periph.io/x/periph/host/sysfs" +) diff --git a/vendor/periph.io/x/periph/host/odroidc1/doc.go b/vendor/periph.io/x/periph/host/odroidc1/doc.go new file mode 100644 index 0000000..c004893 --- /dev/null +++ b/vendor/periph.io/x/periph/host/odroidc1/doc.go @@ -0,0 +1,26 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package odroidc1 contains header definitions for Hardkernel's ODROID C0, C1, +// and C1+ boards. +// +// These boards use an Amlogic S805 processor (called "meson_8b" in the linux +// kernel). Currently no package for memory-mapped I/O has been written for +// this processor, thus all gpio functions are implemented via sysfs. +// +// This package only exports the main J2 header, which is rPi compatible except +// for a couple of analog pins (which are not currently supported). The J2 +// header has two I²C buses on header pins 3/5 and 27/28, the I²C functionality +// can be enabled by loading the aml_i2c kernel module. It has one SPI bus on +// header pins 19/21/23/24. The onewire gpio driver appears to be loaded by +// default on header pin 7. +// +// References +// +// Product page: http://www.hardkernel.com/main/products/prdt_info.php?g_code=G143703355573&tab_idx=2 +// +// Hardware wiki: http://odroid.com/dokuwiki/doku.php?id=en:c1_hardware +// +// Ubuntu drivers: http://odroid.com/dokuwiki/doku.php?id=en:odroid-c1#ubuntu +package odroidc1 diff --git a/vendor/periph.io/x/periph/host/odroidc1/odroidc1.go b/vendor/periph.io/x/periph/host/odroidc1/odroidc1.go new file mode 100644 index 0000000..2c885b4 --- /dev/null +++ b/vendor/periph.io/x/periph/host/odroidc1/odroidc1.go @@ -0,0 +1,195 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package odroidc1 + +import ( + "errors" + "strconv" + "strings" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/conn/pin/pinreg" + "periph.io/x/periph/host/distro" + "periph.io/x/periph/host/sysfs" +) + +// The J2 header is rPi compatible, except for the two analog pins and the 1.8V +// output. +var ( + J2_1 = pin.V3_3 // 3.3V; max 30mA + J2_2 = pin.V5 // 5V (after filtering) + J2_3 gpio.PinIO = gpio.INVALID // I2C1_SDA + J2_4 = pin.V5 // 5V (after filtering) + J2_5 gpio.PinIO = gpio.INVALID // I2C1_SCL + J2_6 = pin.GROUND // + J2_7 gpio.PinIO = gpio.INVALID // CLK0 + J2_8 gpio.PinIO = gpio.INVALID // UART0_TX, UART1_TX + J2_9 = pin.GROUND // + J2_10 gpio.PinIO = gpio.INVALID // UART0_RX, UART1_RX + J2_11 gpio.PinIO = gpio.INVALID // UART0_RTS, SPI1_CS1, UART1_RTS + J2_12 gpio.PinIO = gpio.INVALID // I2S_SCK, SPI1_CS0, PWM0 + J2_13 gpio.PinIO = gpio.INVALID // GPIO116 + J2_14 = pin.GROUND // + J2_15 gpio.PinIO = gpio.INVALID // GPIO115 + J2_16 gpio.PinIO = gpio.INVALID // GPIO104 + J2_17 = pin.V3_3 // + J2_18 gpio.PinIO = gpio.INVALID // GPIO102 + J2_19 gpio.PinIO = gpio.INVALID // SPI0_MOSI + J2_20 = pin.GROUND // + J2_21 gpio.PinIO = gpio.INVALID // SPI0_MISO + J2_22 gpio.PinIO = gpio.INVALID // GPIO103 + J2_23 gpio.PinIO = gpio.INVALID // SPI0_CLK + J2_24 gpio.PinIO = gpio.INVALID // SPI0_CS0 + J2_25 = pin.GROUND // + J2_26 gpio.PinIO = gpio.INVALID // SPI0_CS1 + J2_27 gpio.PinIO = gpio.INVALID // I2C0_SDA + J2_28 gpio.PinIO = gpio.INVALID // I2C0_SCL + J2_29 gpio.PinIO = gpio.INVALID // CLK1 + J2_30 = pin.GROUND // + J2_31 gpio.PinIO = gpio.INVALID // CLK2 + J2_32 gpio.PinIO = gpio.INVALID // PWM0 + J2_33 gpio.PinIO = gpio.INVALID // PWM1 + J2_34 = pin.GROUND // + J2_35 gpio.PinIO = gpio.INVALID // I2S_WS, SPI1_MISO, PWM1 + J2_36 gpio.PinIO = gpio.INVALID // UART0_CTS, SPI1_CS2, UART1_CTS + J2_37 = pin.INVALID // BUG(tve): make pins J2_37 and J2_40 functional once analog support is implemented + J2_38 = pin.V1_8 // + J2_39 = pin.GROUND // + J2_40 = pin.INVALID // See above. +) + +// Present returns true if running on a Hardkernel ODROID-C0/C1/C1+ board. +// +// It looks for "8726_M8B" in the device tree or "ODROIDC" in cpuinfo. The +// following information is expected in the device dtree: +// root@odroid:/proc/device-tree# od -c compatible +// 0000000 A M L O G I C , 8 7 2 6 _ M 8 B +func Present() bool { + for _, c := range distro.DTCompatible() { + if strings.Contains(c, "8726_M8B") { + return true + } + } + return distro.CPUInfo()["Hardware"] == "ODROIDC" +} + +// + +// aliases is a list of aliases for the various gpio pins, this allows users to +// refer to pins using the documented and labeled names instead of some GPIOnnn +// name. The map key is the alias and the value is the real pin name. +var aliases = map[string]int{ + "I2C0_SDA": 74, + "I2C0_SCL": 75, + "I2C1_SDA": 76, + "I2C1_SCL": 77, + "I2CA_SDA": 74, + "I2CA_SCL": 75, + "I2CB_SDA": 76, + "I2CB_SCL": 77, + "SPI0_MOSI": 107, + "SPI0_MISO": 106, + "SPI0_CLK": 105, + "SPI0_CS0": 117, +} + +// sysfsPin is a safe way to get a sysfs pin +func sysfsPin(n int) gpio.PinIO { + if p, ok := sysfs.Pins[n]; ok { + return p + } + return gpio.INVALID +} + +// driver implements drivers.Driver. +type driver struct { +} + +func (d *driver) String() string { + return "odroid-c1" +} + +func (d *driver) Prerequisites() []string { + return nil +} + +func (d *driver) After() []string { + return []string{"sysfs-gpio"} +} + +func (d *driver) Init() (bool, error) { + if !Present() { + return false, errors.New("Hardkernel ODROID-C0/C1/C1+ board not detected") + } + J2_3 = sysfsPin(74) + J2_5 = sysfsPin(75) + J2_7 = sysfsPin(83) // usually taken by 1-wire driver + J2_8 = sysfsPin(113) // usually not available + J2_10 = sysfsPin(114) // usually not available + J2_11 = sysfsPin(88) + J2_12 = sysfsPin(87) + J2_13 = sysfsPin(116) + J2_15 = sysfsPin(115) + J2_16 = sysfsPin(104) + J2_18 = sysfsPin(102) + J2_19 = sysfsPin(107) + J2_21 = sysfsPin(106) + J2_22 = sysfsPin(103) + J2_23 = sysfsPin(105) + J2_24 = sysfsPin(117) + J2_26 = sysfsPin(118) + J2_27 = sysfsPin(76) + J2_28 = sysfsPin(77) + J2_29 = sysfsPin(101) + J2_31 = sysfsPin(100) + J2_32 = sysfsPin(99) + J2_33 = sysfsPin(108) + J2_35 = sysfsPin(97) + J2_36 = sysfsPin(98) + + // J2 is the 40-pin rPi-compatible header + J2 := [][]pin.Pin{ + {J2_1, J2_2}, + {J2_3, J2_4}, + {J2_5, J2_6}, + {J2_7, J2_8}, + {J2_9, J2_10}, + {J2_11, J2_12}, + {J2_13, J2_14}, + {J2_15, J2_16}, + {J2_17, J2_18}, + {J2_19, J2_20}, + {J2_21, J2_22}, + {J2_23, J2_24}, + {J2_25, J2_26}, + {J2_27, J2_28}, + {J2_29, J2_30}, + {J2_31, J2_32}, + {J2_33, J2_34}, + {J2_35, J2_36}, + {J2_37, J2_38}, + {J2_39, J2_40}, + } + if err := pinreg.Register("J2", J2); err != nil { + return true, err + } + for alias, number := range aliases { + if err := gpioreg.RegisterAlias(alias, strconv.Itoa(number)); err != nil { + return true, err + } + } + return true, nil +} + +func init() { + if isArm { + periph.MustRegister(&drv) + } +} + +var drv driver diff --git a/vendor/periph.io/x/periph/host/odroidc1/odroidc1_arm.go b/vendor/periph.io/x/periph/host/odroidc1/odroidc1_arm.go new file mode 100644 index 0000000..604d3f8 --- /dev/null +++ b/vendor/periph.io/x/periph/host/odroidc1/odroidc1_arm.go @@ -0,0 +1,7 @@ +// Copyright 2016 The PIO Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package odroidc1 + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/odroidc1/odroidc1_other.go b/vendor/periph.io/x/periph/host/odroidc1/odroidc1_other.go new file mode 100644 index 0000000..8b57cde --- /dev/null +++ b/vendor/periph.io/x/periph/host/odroidc1/odroidc1_other.go @@ -0,0 +1,9 @@ +// Copyright 2016 The PIO Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm + +package odroidc1 + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/pine64/doc.go b/vendor/periph.io/x/periph/host/pine64/doc.go new file mode 100644 index 0000000..251aa2b --- /dev/null +++ b/vendor/periph.io/x/periph/host/pine64/doc.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package pine64 contains Pine64 hardware logic. It is intrinsically +// related to package a64. +// +// Requires Armbian Jessie Server. +// +// Physical +// +// http://files.pine64.org/doc/Pine%20A64%20Schematic/Pine%20A64%20Pin%20Assignment%20160119.pdf +// +// http://wiki.pine64.org/images/2/2e/Pine64_Board_Connector_heatsink.png +package pine64 diff --git a/vendor/periph.io/x/periph/host/pine64/pine64.go b/vendor/periph.io/x/periph/host/pine64/pine64.go new file mode 100644 index 0000000..fb9051e --- /dev/null +++ b/vendor/periph.io/x/periph/host/pine64/pine64.go @@ -0,0 +1,272 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pine64 + +import ( + "errors" + "os" + + "periph.io/x/periph" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/conn/pin/pinreg" + "periph.io/x/periph/host/allwinner" +) + +// Present returns true if running on a Pine64 board. +// +// https://www.pine64.org/ +func Present() bool { + if isArm { + // This is iffy at best. + _, err := os.Stat("/boot/pine64.dtb") + return err == nil + } + return false +} + +// Pine64 specific pins. +var ( + VCC = &pin.BasicPin{N: "VCC"} // + IOVCC = &pin.BasicPin{N: "IOVCC"} // Power supply for port A + TEMP_SENSOR = &pin.BasicPin{N: "TEMP_SENSOR"} // + IR_RX = &pin.BasicPin{N: "IR_RX"} // IR Data Receive + CHARGER_LED = &pin.BasicPin{N: "CHARGER_LED"} // + RESET = &pin.BasicPin{N: "RESET"} // + PWR_SWITCH = &pin.BasicPin{N: "PWR_SWITCH "} // +) + +// All the individual pins on the headers. +var ( + P1_1 = pin.V3_3 // max 40mA + P1_2 = pin.V5 // (filtered) + P1_3 = allwinner.PH3 // + P1_4 = pin.V5 // (filtered) + P1_5 = allwinner.PH2 // + P1_6 = pin.GROUND // + P1_7 = allwinner.PL10 // + P1_8 = allwinner.PB0 // + P1_9 = pin.GROUND // + P1_10 = allwinner.PB1 // + P1_11 = allwinner.PC7 // + P1_12 = allwinner.PC8 // + P1_13 = allwinner.PH9 // + P1_14 = pin.GROUND // + P1_15 = allwinner.PC12 // + P1_16 = allwinner.PC13 // + P1_17 = pin.V3_3 // + P1_18 = allwinner.PC14 // + P1_19 = allwinner.PC0 // + P1_20 = pin.GROUND // + P1_21 = allwinner.PC1 // + P1_22 = allwinner.PC15 // + P1_23 = allwinner.PC2 // + P1_24 = allwinner.PC3 // + P1_25 = pin.GROUND // + P1_26 = allwinner.PH7 // + P1_27 = allwinner.PL9 // + P1_28 = allwinner.PL8 // + P1_29 = allwinner.PH5 // + P1_30 = pin.GROUND // + P1_31 = allwinner.PH6 // + P1_32 = allwinner.PC4 // + P1_33 = allwinner.PC5 // + P1_34 = pin.GROUND // + P1_35 = allwinner.PC9 // + P1_36 = allwinner.PC6 // + P1_37 = allwinner.PC16 // + P1_38 = allwinner.PC10 // + P1_39 = pin.GROUND // + P1_40 = allwinner.PC11 // + + EULER_1 = pin.V3_3 // + EULER_2 = pin.DC_IN // + EULER_3 = pin.BAT_PLUS // + EULER_4 = pin.DC_IN // + EULER_5 = TEMP_SENSOR // + EULER_6 = pin.GROUND // + EULER_7 = IR_RX // + EULER_8 = pin.V5 // + EULER_9 = pin.GROUND // + EULER_10 = allwinner.PH8 // + EULER_11 = allwinner.PB3 // + EULER_12 = allwinner.PB4 // + EULER_13 = allwinner.PB5 // + EULER_14 = pin.GROUND // + EULER_15 = allwinner.PB6 // + EULER_16 = allwinner.PB7 // + EULER_17 = pin.V3_3 // + EULER_18 = allwinner.PD4 // + EULER_19 = allwinner.PD2 // + EULER_20 = pin.GROUND // + EULER_21 = allwinner.PD3 // + EULER_22 = allwinner.PD5 // + EULER_23 = allwinner.PD1 // + EULER_24 = allwinner.PD0 // + EULER_25 = pin.GROUND // + EULER_26 = allwinner.PD6 // + EULER_27 = allwinner.PB2 // + EULER_28 = allwinner.PD7 // + EULER_29 = allwinner.PB8 // + EULER_30 = allwinner.PB9 // + EULER_31 = allwinner.EAROUTP // + EULER_32 = allwinner.EAROUTN // + EULER_33 = pin.INVALID // + EULER_34 = pin.GROUND // + + EXP_1 = pin.V3_3 // + EXP_2 = allwinner.PL7 // + EXP_3 = CHARGER_LED // + EXP_4 = RESET // + EXP_5 = PWR_SWITCH // + EXP_6 = pin.GROUND // + EXP_7 = allwinner.PB8 // + EXP_8 = allwinner.PB9 // + EXP_9 = pin.GROUND // + EXP_10 = allwinner.KEY_ADC // + + WIFI_BT_1 = pin.GROUND // + WIFI_BT_2 = allwinner.PG6 // + WIFI_BT_3 = allwinner.PG0 // + WIFI_BT_4 = allwinner.PG7 // + WIFI_BT_5 = pin.GROUND // + WIFI_BT_6 = allwinner.PG8 // + WIFI_BT_7 = allwinner.PG1 // + WIFI_BT_8 = allwinner.PG9 // + WIFI_BT_9 = allwinner.PG2 // + WIFI_BT_10 = allwinner.PG10 // + WIFI_BT_11 = allwinner.PG3 // + WIFI_BT_12 = allwinner.PG11 // + WIFI_BT_13 = allwinner.PG4 // + WIFI_BT_14 = allwinner.PG12 // + WIFI_BT_15 = allwinner.PG5 // + WIFI_BT_16 = allwinner.PG13 // + WIFI_BT_17 = allwinner.PL2 // + WIFI_BT_18 = pin.GROUND // + WIFI_BT_19 = allwinner.PL3 // + WIFI_BT_20 = allwinner.PL5 // + WIFI_BT_21 = allwinner.X32KFOUT // + WIFI_BT_22 = allwinner.PL5 // + WIFI_BT_23 = pin.GROUND // + WIFI_BT_24 = allwinner.PL6 // + WIFI_BT_25 = VCC // + WIFI_BT_26 = IOVCC // + + AUDIO_LEFT = pin.INVALID // BUG(maruel): Fix once analog is implemented. + AUDIO_RIGHT = pin.INVALID // +) + +// + +// driver implements periph.Driver. +type driver struct { +} + +func (d *driver) String() string { + return "pine64" +} + +func (d *driver) Prerequisites() []string { + return nil +} + +func (d *driver) After() []string { + return []string{"allwinner-gpio", "allwinner-gpio-pl"} +} + +func (d *driver) Init() (bool, error) { + if !Present() { + return false, errors.New("pine64 board not detected") + } + if err := pinreg.Register("P1", [][]pin.Pin{ + {P1_1, P1_2}, + {P1_3, P1_4}, + {P1_5, P1_6}, + {P1_7, P1_8}, + {P1_9, P1_10}, + {P1_11, P1_12}, + {P1_13, P1_14}, + {P1_15, P1_16}, + {P1_17, P1_18}, + {P1_19, P1_20}, + {P1_21, P1_22}, + {P1_23, P1_24}, + {P1_25, P1_26}, + {P1_27, P1_28}, + {P1_29, P1_30}, + {P1_31, P1_32}, + {P1_33, P1_34}, + {P1_35, P1_36}, + {P1_37, P1_38}, + {P1_39, P1_40}, + }); err != nil { + return true, err + } + if err := pinreg.Register("EULER", [][]pin.Pin{ + {EULER_1, EULER_2}, + {EULER_3, EULER_4}, + {EULER_5, EULER_6}, + {EULER_7, EULER_8}, + {EULER_9, EULER_10}, + {EULER_11, EULER_12}, + {EULER_13, EULER_14}, + {EULER_15, EULER_16}, + {EULER_17, EULER_18}, + {EULER_19, EULER_20}, + {EULER_21, EULER_22}, + {EULER_23, EULER_24}, + {EULER_25, EULER_26}, + {EULER_27, EULER_28}, + {EULER_29, EULER_30}, + {EULER_31, EULER_32}, + {EULER_33, EULER_34}, + }); err != nil { + return true, err + } + + if err := pinreg.Register("EXP", [][]pin.Pin{ + {EXP_1, EXP_2}, + {EXP_3, EXP_4}, + {EXP_5, EXP_6}, + {EXP_7, EXP_8}, + {EXP_9, EXP_10}, + }); err != nil { + return true, err + } + + if err := pinreg.Register("WIFI_BT", [][]pin.Pin{ + {WIFI_BT_1, WIFI_BT_2}, + {WIFI_BT_3, WIFI_BT_4}, + {WIFI_BT_5, WIFI_BT_6}, + {WIFI_BT_7, WIFI_BT_8}, + {WIFI_BT_9, WIFI_BT_10}, + {WIFI_BT_11, WIFI_BT_12}, + {WIFI_BT_13, WIFI_BT_14}, + {WIFI_BT_15, WIFI_BT_16}, + {WIFI_BT_17, WIFI_BT_18}, + {WIFI_BT_19, WIFI_BT_20}, + {WIFI_BT_21, WIFI_BT_22}, + {WIFI_BT_23, WIFI_BT_24}, + {WIFI_BT_25, WIFI_BT_26}, + }); err != nil { + return true, err + } + + if err := pinreg.Register("AUDIO", [][]pin.Pin{ + {AUDIO_LEFT}, + {AUDIO_RIGHT}, + }); err != nil { + return true, err + } + + return true, nil +} + +func init() { + if isArm { + periph.MustRegister(&drv) + } +} + +var drv driver diff --git a/vendor/periph.io/x/periph/host/pine64/pine64_arm.go b/vendor/periph.io/x/periph/host/pine64/pine64_arm.go new file mode 100644 index 0000000..dcf8013 --- /dev/null +++ b/vendor/periph.io/x/periph/host/pine64/pine64_arm.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pine64 + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/pine64/pine64_arm64.go b/vendor/periph.io/x/periph/host/pine64/pine64_arm64.go new file mode 100644 index 0000000..01a13cd --- /dev/null +++ b/vendor/periph.io/x/periph/host/pine64/pine64_arm64.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build arm64 + +package pine64 + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/pine64/pine64_other.go b/vendor/periph.io/x/periph/host/pine64/pine64_other.go new file mode 100644 index 0000000..8c99603 --- /dev/null +++ b/vendor/periph.io/x/periph/host/pine64/pine64_other.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm,!arm64 + +package pine64 + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/pmem/alloc.go b/vendor/periph.io/x/periph/host/pmem/alloc.go new file mode 100644 index 0000000..7ed5c07 --- /dev/null +++ b/vendor/periph.io/x/periph/host/pmem/alloc.go @@ -0,0 +1,164 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pmem + +import ( + "io" + "reflect" + "unsafe" +) + +const pageSize = 4096 + +// Mem represents a section of memory that is usable by the DMA controller. +// +// Since this is physically allocated memory, that could potentially have been +// allocated in spite of OS consent, for example by asking the GPU directly, it +// is important to call Close() before process exit. +type Mem interface { + io.Closer + // Bytes returns the user space memory mapped buffer address as a slice of + // bytes. + // + // It is the raw view of the memory from this process. + Bytes() []byte + // AsPOD initializes a pointer to a POD (plain old data) to point to the + // memory mapped region. + // + // pp must be a pointer to: + // + // - pointer to a base size type (uint8, int64, float32, etc) + // - struct + // - array of the above + // - slice of the above + // + // and the value must be nil. Returns an error otherwise. + // + // If a pointer to a slice is passed in, it is initialized to the length and + // capacity set to the maximum number of elements this slice can represent. + // + // The pointer initialized points to the same address as Bytes(). + AsPOD(pp interface{}) error + // PhysAddr is the physical address. It can be either 32 bits or 64 bits, + // depending on the bitness of the OS kernel, not on the user mode build, + // e.g. you could have compiled on a 32 bits Go toolchain but running on a + // 64 bits kernel. + PhysAddr() uint64 +} + +// MemAlloc represents contiguous physically locked memory that was allocated. +// +// The memory is mapped in user space. +// +// MemAlloc implements Mem. +type MemAlloc struct { + View +} + +// Close unmaps the physical memory allocation. +func (m *MemAlloc) Close() error { + if err := munlock(m.orig); err != nil { + return err + } + return munmap(m.orig) +} + +// Alloc allocates a continuous chunk of physical memory. +// +// Size must be rounded to 4Kb. Allocations of 4Kb will normally succeed. +// Allocations larger than 64Kb will likely fail due to kernel memory +// fragmentation; rebooting the host or reducing the number of running programs +// may help. +// +// The allocated memory is uncached. +func Alloc(size int) (*MemAlloc, error) { + if size == 0 || size&(pageSize-1) != 0 { + return nil, wrapf("allocated memory must be rounded to %d bytes", pageSize) + } + if isLinux { + return allocLinux(size) + } + return nil, wrapf("memory allocation is not supported on this platform") +} + +// + +// uallocMemLocked allocates user space memory and requests the OS to have the +// chunk to be locked into physical memory. +func uallocMemLocked(size int) ([]byte, error) { + // It is important to write to the memory so it is forced to be present. + b, err := uallocMem(size) + if err == nil { + for i := range b { + b[i] = 0 + } + if err := mlock(b); err != nil { + // Ignore the unmap error. + _ = munmap(b) + return nil, wrapf("locking %d bytes failed: %v", size, err) + } + } + return b, err +} + +// allocLinux allocates physical memory and returns a user view to it. +func allocLinux(size int) (*MemAlloc, error) { + // TODO(maruel): Implement the "shotgun approach". Allocate a ton of 4Kb + // pages and lock them. Then look at their physical pages and only keep the + // one useful. Then create a linear mapping in memory to simplify the user + // mode with a single linear user space virtual address but keep the + // individual page alive with their initial allocation. When done release + // each individual page. + if size > pageSize { + return nil, wrapf("large allocation is not yet implemented") + } + // First allocate a chunk of user space memory. + b, err := uallocMemLocked(size) + if err != nil { + return nil, err + } + pages := make([]uint64, (size+pageSize-1)/pageSize) + // Figure out the physical memory addresses. + for i := range pages { + pages[i], err = virtToPhys(toRaw(b[pageSize*i:])) + if err != nil { + return nil, err + } + if pages[i] == 0 { + return nil, wrapf("failed to read page %d", i) + } + } + for i := 1; i < len(pages); i++ { + // Fail if the memory is not contiguous. + if pages[i] != pages[i-1]+pageSize { + return nil, wrapf("failed to allocate %d bytes of continugous physical memory; page %d =0x%x; page %d=0x%x", size, i, pages[i], i-1, pages[i-1]) + } + } + + return &MemAlloc{View{Slice: b, phys: pages[0], orig: b}}, nil +} + +// virtToPhys returns the physical memory address backing a virtual +// memory address. +func virtToPhys(virt uintptr) (uint64, error) { + physPage, err := ReadPageMap(virt) + if err != nil { + return 0, err + } + if physPage&(1<<63) == 0 { + // If high bit is not set, the page doesn't exist. + return 0, wrapf("0x%08x has no physical address", virt) + } + // Strip flags. See linux documentation on kernel.org for more details. + physPage &^= 0x1FF << 55 + return physPage * pageSize, nil +} + +func toRaw(b []byte) uintptr { + header := *(*reflect.SliceHeader)(unsafe.Pointer(&b)) + return header.Data +} + +var _ Mem = &MemAlloc{} diff --git a/vendor/periph.io/x/periph/host/pmem/doc.go b/vendor/periph.io/x/periph/host/pmem/doc.go new file mode 100644 index 0000000..b16c664 --- /dev/null +++ b/vendor/periph.io/x/periph/host/pmem/doc.go @@ -0,0 +1,69 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package pmem implements handling of physical memory for user space programs. +// +// To make things confusing, a modern computer has many view of the memory +// (address spaces): +// +// User +// +// User mode address space is the virtual address space that an application +// runs in. It is generally a tad less than half the addressable memory, so on +// a 32 bits system, the addressable range is 1.9Gb. For 64 bits OS, it depends +// but it usually at least 3.5Gb. The memory is virtual and can be flushed to +// disk in the swap file unless individual pages are locked. +// +// Kernel +// +// Kernel address space is the virtual address space the kernel sees. It often +// can see the currently active user space program on the current CPU core in +// addition to all the memory the kernel sees. The kernel memory pages that are +// not mlock()'ed are 'virtual' and can be flushed to disk in the swap file +// when there's not enough RAM available. On linux systems, the kernel +// addressed memory can be mapped in user space via `/dev/kmem`. +// +// Physical +// +// Physical memory address space is the actual address of each page in the DRAM +// chip and anything connected to the memory controller. The mapping may be +// different depending on what controller looks at the bus, like with IOMMU. So +// a peripheral (GPU, DMA controller) may have a different view of the physical +// memory than the host CPU. On linux systems, this memory can be mapped in +// user space via `/dev/mem`. +// +// CPU +// +// The CPU or its subsystems may memory map registers (for example, to control +// GPIO pins, clock speed, etc). This is not "real" memory, this is a view of +// registers but it still follows "mostly" the same semantic as DRAM backed +// physical memory. +// +// Some CPU memory may have very special semantic where the mere fact of +// reading has side effects. For example reading a specific register may +// latches another. +// +// CPU memory accesses are layered with multiple caches, usually named L1, L2 +// and optionally L3. Some controllers (DMA) can see some cache levels (L2) but +// not others (L1) on some CPU architecture (bcm283x). This means that a user +// space program writing data to a memory page and immediately asking the DMA +// controller to read it may cause stale data to be read! +// +// Hypervisor +// +// Hypervisor can change the complete memory mapping as seen by the kernel. +// This is outside the scope of this project. :) +// +// Summary +// +// In practice, the semantics change between CPU manufacturers (Broadcom vs +// Allwinner) and between architectures (ARM vs x86). The most tricky one is to +// understand cached memory and how it affects coherence and performance. +// Uncached memory is extremely slow so it must only be used when necessary. +// +// References +// +// Overview of IOMMU: +// https://en.wikipedia.org/wiki/Input-output_memory_management_unit +package pmem diff --git a/vendor/periph.io/x/periph/host/pmem/mem_linux.go b/vendor/periph.io/x/periph/host/pmem/mem_linux.go new file mode 100644 index 0000000..fca165c --- /dev/null +++ b/vendor/periph.io/x/periph/host/pmem/mem_linux.go @@ -0,0 +1,56 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pmem + +import "syscall" + +const isLinux = true + +func mmap(fd uintptr, offset int64, length int) ([]byte, error) { + v, err := syscall.Mmap(int(fd), offset, length, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) + if err != nil { + return nil, wrapf("failed to memory map: %v", err) + } + return v, nil +} + +func munmap(b []byte) error { + if err := syscall.Munmap(b); err != nil { + return wrapf("failed to unmap memory: %v", err) + } + return nil + +} + +func mlock(b []byte) error { + if err := syscall.Mlock(b); err != nil { + return wrapf("failed to lock memory: %v", err) + } + return nil +} + +func munlock(b []byte) error { + if err := syscall.Munlock(b); err != nil { + return wrapf("failed to unlock memory: %v", err) + } + return nil +} + +// uallocMem allocates user space memory. +func uallocMem(size int) ([]byte, error) { + b, err := syscall.Mmap( + 0, + 0, + size, + syscall.PROT_READ|syscall.PROT_WRITE, + syscall.MAP_ANONYMOUS|syscall.MAP_LOCKED|syscall.MAP_NORESERVE|syscall.MAP_SHARED) + // syscall.MAP_HUGETLB / MAP_HUGE_2MB + // See /sys/kernel/mm/hugepages but both C.H.I.P. running Jessie and Raspbian + // Jessie do not expose huge pages. :( + if err != nil { + return nil, wrapf("allocating %d bytes failed: %v", size, err) + } + return b, err +} diff --git a/vendor/periph.io/x/periph/host/pmem/mem_other.go b/vendor/periph.io/x/periph/host/pmem/mem_other.go new file mode 100644 index 0000000..ce78a85 --- /dev/null +++ b/vendor/periph.io/x/periph/host/pmem/mem_other.go @@ -0,0 +1,30 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !linux + +package pmem + +const isLinux = false + +func mmap(fd uintptr, offset int64, length int) ([]byte, error) { + return nil, wrapf("syscall.Mmap() not implemented on this OS") +} + +func munmap(b []byte) error { + return wrapf("syscall.Munmap() not implemented on this OS") +} + +func mlock(b []byte) error { + return wrapf("syscall.Mlock() not implemented on this OS") +} + +func munlock(b []byte) error { + return wrapf("syscall.Munlock() not implemented on this OS") +} + +// uallocMem allocates user space memory. +func uallocMem(size int) ([]byte, error) { + return make([]byte, size), nil +} diff --git a/vendor/periph.io/x/periph/host/pmem/pagemap.go b/vendor/periph.io/x/periph/host/pmem/pagemap.go new file mode 100644 index 0000000..0513598 --- /dev/null +++ b/vendor/periph.io/x/periph/host/pmem/pagemap.go @@ -0,0 +1,64 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pmem + +import ( + "encoding/binary" + "errors" + "fmt" + "os" +) + +// ReadPageMap reads a physical address mapping for a virtual page address from +// /proc/self/pagemap. +// +// It returns the physical address that corresponds to the start of the virtual +// page within which the virtual address virtAddr is located. +// +// The meaning of the return value is documented at +// https://www.kernel.org/doc/Documentation/vm/pagemap.txt +func ReadPageMap(virtAddr uintptr) (uint64, error) { + if !isLinux { + return 0, errors.New("pmem: pagemap is not supported on this platform") + } + return readPageMapLinux(virtAddr) +} + +// + +var ( + pageMap fileIO + pageMapErr error +) + +func readPageMapLinux(virtAddr uintptr) (uint64, error) { + var b [8]byte + mu.Lock() + defer mu.Unlock() + if pageMap == nil && pageMapErr == nil { + // Open /proc/self/pagemap. + // + // It is a uint64 array where the index represents the virtual 4Kb page + // number and the value represents the physical page properties backing + // this virtual page. + pageMap, pageMapErr = openFile("/proc/self/pagemap", os.O_RDONLY|os.O_SYNC) + } + if pageMapErr != nil { + return 0, pageMapErr + } + // Convert address to page number, then index in uint64 array. + offset := int64(virtAddr / pageSize * 8) + if _, err := pageMap.Seek(offset, os.SEEK_SET); err != nil { + return 0, fmt.Errorf("pmem: failed to seek at 0x%x for 0x%x: %v", offset, virtAddr, err) + } + n, err := pageMap.Read(b[:]) + if err != nil { + return 0, fmt.Errorf("pmem: failed to read at 0x%x for 0x%x: %v", offset, virtAddr, err) + } + if n != len(b) { + return 0, fmt.Errorf("pmem: failed to read the amount of data %d", len(b)) + } + return binary.LittleEndian.Uint64(b[:]), nil +} diff --git a/vendor/periph.io/x/periph/host/pmem/smoketest.go b/vendor/periph.io/x/periph/host/pmem/smoketest.go new file mode 100644 index 0000000..7e184af --- /dev/null +++ b/vendor/periph.io/x/periph/host/pmem/smoketest.go @@ -0,0 +1,89 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pmem + +import ( + "bytes" + "math/rand" +) + +// TestCopy is used by CPU drivers to verify that the DMA engine works +// correctly. +// +// It is not meant to be used by end users. +// +// TestCopy allocates two buffer via `alloc`, once as the source and one as the +// destination. It fills the source with random data and the destination with +// 0x11. +// +// `copyMem` is expected to copy the memory from pSrc to pDst, with an offset +// of `hole` and size `size-2*hole`. +// +// The function `copyMem` being tested is only given the buffer physical +// addresses and must copy the data without other help. It is expected to +// +// This confirm misaligned DMA copying works. +// leverage the host's DMA engine. +func TestCopy(size, holeSize int, alloc func(size int) (Mem, error), copyMem func(pDst, pSrc uint64) error) error { + pSrc, err2 := alloc(size) + if err2 != nil { + return err2 + } + defer pSrc.Close() + pDst, err2 := alloc(size) + if err2 != nil { + return err2 + } + defer pDst.Close() + dst := pDst.Bytes() + for i := range dst { + dst[i] = 0x11 + } + src := make([]byte, size) + for i := range src { + src[i] = byte(rand.Int31()) + } + copy(pSrc.Bytes(), src[:]) + + // Run the driver supplied memory copying code. + if err := copyMem(pDst.PhysAddr(), pSrc.PhysAddr()); err != nil { + return err + } + + // Verifications. + for i := 0; i < holeSize; i++ { + if dst[i] != 0x11 { + return wrapf("DMA corrupted the buffer header: %x", dst[:holeSize]) + } + if dst[size-1-i] != 0x11 { + return wrapf("DMA corrupted the buffer footer: %x", dst[size-1-holeSize:]) + } + } + + // Headers and footers were not corupted in the destination. Verify the inner + // view that should match. + x := src[:size-2*holeSize] + y := dst[holeSize : size-holeSize] + if !bytes.Equal(x, y) { + offset := 0 + for len(x) != 0 && x[0] == y[0] { + x = x[1:] + y = y[1:] + offset++ + } + for len(x) != 0 && x[len(x)-1] == y[len(y)-1] { + x = x[:len(x)-1] + y = y[:len(y)-1] + } + if len(x) > 32 { + x = x[:32] + } + if len(y) > 32 { + y = y[:32] + } + return wrapf("DMA corrupted the buffer at offset %d:\n%x\n%x", offset, x, y) + } + return nil +} diff --git a/vendor/periph.io/x/periph/host/pmem/view.go b/vendor/periph.io/x/periph/host/pmem/view.go new file mode 100644 index 0000000..6d207ed --- /dev/null +++ b/vendor/periph.io/x/periph/host/pmem/view.go @@ -0,0 +1,283 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package pmem + +import ( + "fmt" + "io" + "os" + "reflect" + "sync" + "unsafe" + + "periph.io/x/periph/host/fs" +) + +// Slice can be transparently viewed as []byte, []uint32 or a struct. +type Slice []byte + +// Uint32 returns a view of the byte slice as a []uint32. +func (s *Slice) Uint32() []uint32 { + header := *(*reflect.SliceHeader)(unsafe.Pointer(s)) + header.Len /= 4 + header.Cap /= 4 + return *(*[]uint32)(unsafe.Pointer(&header)) +} + +// Bytes implements Mem. +func (s *Slice) Bytes() []byte { + return *s +} + +// AsPOD implements Mem. +func (s *Slice) AsPOD(pp interface{}) error { + if pp == nil { + return wrapf("require Ptr, got nil") + } + vpp := reflect.ValueOf(pp) + if elemSize, err := isPS(len(*s), vpp); err == nil { + p := vpp.Elem() + t := p.Type().Elem() + if elemSize > len(*s) { + return wrapf("can't map slice of struct %s (size %d) on [%d]byte", t, elemSize, len(*s)) + } + nbElems := len(*s) / elemSize + // Use casting black magic to set the internal slice headers. + hdr := (*reflect.SliceHeader)(unsafe.Pointer(p.UnsafeAddr())) + hdr.Data = ((*reflect.SliceHeader)(unsafe.Pointer(s))).Data + hdr.Len = nbElems + hdr.Cap = nbElems + return nil + } + + size, err := isPP(vpp) + if err != nil { + return err + } + p := vpp.Elem() + t := p.Type().Elem() + if size > len(*s) { + return wrapf("can't map struct %s (size %d) on [%d]byte", t, size, len(*s)) + } + // Use casting black magic to read the internal slice headers. + dest := unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(s))).Data) + // Use reflection black magic to write to the original pointer. + p.Set(reflect.NewAt(t, dest)) + return nil +} + +// View represents a view of physical memory memory mapped into user space. +// +// It is usually used to map CPU registers into user space, usually I/O +// registers and the likes. +// +// It is not required to call Close(), the kernel will clean up on process +// shutdown. +type View struct { + Slice + orig []uint8 // Reference rounded to the lowest 4Kb page containing Slice. + phys uint64 // physical address of the base of Slice. +} + +// Close unmaps the memory from the user address space. +// +// This is done naturally by the OS on process teardown (when the process +// exits) so this is not a hard requirement to call this function. +func (v *View) Close() error { + return munmap(v.orig) +} + +// PhysAddr implements Mem. +func (v *View) PhysAddr() uint64 { + return v.phys +} + +// MapGPIO returns a CPU specific memory mapping of the CPU I/O registers using +// /dev/gpiomem. +// +// At the moment, /dev/gpiomem is only supported on Raspbian Jessie via a +// specific kernel driver. +func MapGPIO() (*View, error) { + if isLinux { + return mapGPIOLinux() + } + return nil, wrapf("/dev/gpiomem is not supported on this platform") +} + +// Map returns a memory mapped view of arbitrary physical memory range using OS +// provided functionality. +// +// Maps size of memory, rounded on a 4kb window. +// +// This function is dangerous and should be used wisely. It normally requires +// super privileges (root). On Linux, it leverages /dev/mem. +func Map(base uint64, size int) (*View, error) { + if isLinux { + return mapLinux(base, size) + } + return nil, wrapf("physical memory mapping is not supported on this platform") +} + +// MapAsPOD is a leaky shorthand of calling Map(base, sizeof(v)) then AsPOD(v). +// +// There is no way to reclaim the memory map. +// +// A slice cannot be used, as it does not have inherent size. Use an aray +// instead. +func MapAsPOD(base uint64, i interface{}) error { + // Automatically determine the necessary size. Because of this, slice of + // unspecified length cannot be used here. + if i == nil { + return wrapf("require Ptr, got nil") + } + v := reflect.ValueOf(i) + size, err := isPP(v) + if err != nil { + return err + } + m, err := Map(base, size) + if err != nil { + return err + } + return m.AsPOD(i) +} + +// + +// Keep a cache of open file handles instead of opening and closing repeatedly. +var ( + mu sync.Mutex + gpioMemErr error + gpioMemView *View + devMem fileIO + devMemErr error + openFile = openFileOrig +) + +type fileIO interface { + io.Closer + io.Seeker + io.Reader + Fd() uintptr +} + +func openFileOrig(path string, flag int) (fileIO, error) { + f, err := fs.Open(path, flag) + if err != nil { + return nil, err + } + return f, nil +} + +// mapGPIOLinux is purely Raspbian specific. +func mapGPIOLinux() (*View, error) { + mu.Lock() + defer mu.Unlock() + if gpioMemView == nil && gpioMemErr == nil { + if f, err := openFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC); err == nil { + defer f.Close() + if i, err := mmap(f.Fd(), 0, pageSize); err == nil { + gpioMemView = &View{Slice: i, orig: i, phys: 0} + } else { + gpioMemErr = wrapf("failed to memory map in user space GPIO memory: %v", err) + } + } else { + gpioMemErr = wrapf("failed to open GPIO memory: %v", err) + } + } + return gpioMemView, gpioMemErr +} + +// mapLinux leverages /dev/mem to map a view of physical memory. +func mapLinux(base uint64, size int) (*View, error) { + f, err := openDevMemLinux() + if err != nil { + return nil, err + } + // Align base and size at 4Kb. + offset := int(base & 0xFFF) + i, err := mmap(f.Fd(), int64(base&^0xFFF), (size+offset+0xFFF)&^0xFFF) + if err != nil { + return nil, wrapf("mapping at 0x%x failed: %v", base, err) + } + return &View{Slice: i[offset : offset+size], orig: i, phys: base + uint64(offset)}, nil +} + +func openDevMemLinux() (fileIO, error) { + mu.Lock() + defer mu.Unlock() + if devMem == nil && devMemErr == nil { + if devMem, devMemErr = openFile("/dev/mem", os.O_RDWR|os.O_SYNC); devMemErr != nil { + devMemErr = wrapf("failed to open physical memory: %v", devMemErr) + } + } + return devMem, devMemErr +} + +func isAcceptableInner(t reflect.Type) error { + switch k := t.Kind(); k { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + return nil + case reflect.Array: + return isAcceptableInner(t.Elem()) + case reflect.Struct: + for i := 0; i < t.NumField(); i++ { + if err := isAcceptableInner(t.Field(i).Type); err != nil { + return err + } + } + return nil + default: + return wrapf("require Ptr to Ptr to a POD type, got Ptr to Ptr to %s", k) + } +} + +// isPP makes sure it is a pointer to a nil-pointer to something. It does +// sanity checks to reduce likelihood of a panic(). +func isPP(pp reflect.Value) (int, error) { + if k := pp.Kind(); k != reflect.Ptr { + return 0, wrapf("require Ptr, got %s of %s", k, pp.Type().Name()) + } + p := pp.Elem() + if k := p.Kind(); k != reflect.Ptr { + return 0, wrapf("require Ptr to Ptr, got %s", k) + } + if !p.IsNil() { + return 0, wrapf("require Ptr to Ptr to be nil") + } + // p.Elem() can't be used since it's a nil pointer. Use the type instead. + t := p.Type().Elem() + if err := isAcceptableInner(t); err != nil { + return 0, err + } + return int(t.Size()), nil +} + +// isPS makes sure it is a pointer to a nil-slice of something. It does +// sanity checks to reduce likelihood of a panic(). +func isPS(bufSize int, ps reflect.Value) (int, error) { + if k := ps.Kind(); k != reflect.Ptr { + return 0, wrapf("require Ptr, got %s of %s", k, ps.Type().Name()) + } + s := ps.Elem() + if k := s.Kind(); k != reflect.Slice { + return 0, wrapf("require Ptr to Slice, got %s", k) + } + if !s.IsNil() { + return 0, wrapf("require Ptr to Slice to be nil") + } + // s.Elem() can't be used since it's a nil slice. Use the type instead. + t := s.Type().Elem() + if err := isAcceptableInner(t); err != nil { + return 0, err + } + return int(t.Size()), nil +} + +func wrapf(format string, a ...interface{}) error { + return fmt.Errorf("pmem: "+format, a...) +} diff --git a/vendor/periph.io/x/periph/host/rpi/doc.go b/vendor/periph.io/x/periph/host/rpi/doc.go new file mode 100644 index 0000000..8730b9d --- /dev/null +++ b/vendor/periph.io/x/periph/host/rpi/doc.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package rpi contains Raspberry Pi hardware logic. It is intrinsically +// related to package bcm283x. +// +// Assumes Raspbian but does not directly depend on the distro being Raspbian. +// Windows IoT is currently not supported. +// +// Physical +// +// The physical pin out is based on http://www.raspberrypi.org information but +// http://pinout.xyz/ has a nice interactive web page. +package rpi diff --git a/vendor/periph.io/x/periph/host/rpi/rpi.go b/vendor/periph.io/x/periph/host/rpi/rpi.go new file mode 100644 index 0000000..02f95c5 --- /dev/null +++ b/vendor/periph.io/x/periph/host/rpi/rpi.go @@ -0,0 +1,657 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Raspberry Pi pin out. + +package rpi + +import ( + "errors" + "fmt" + "os" + "strconv" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/conn/pin/pinreg" + "periph.io/x/periph/host/bcm283x" + "periph.io/x/periph/host/distro" +) + +// Present returns true if running on a Raspberry Pi board. +// +// https://www.raspberrypi.org/ +func Present() bool { + if isArm { + // This is iffy at best. + _, err := os.Stat("/sys/bus/platform/drivers/raspberrypi-firmware") + return err == nil + } + return false +} + +// Pin as connect on the 40 pins extension header. +// +// Schematics are useful to know what is connected to what: +// https://www.raspberrypi.org/documentation/hardware/raspberrypi/schematics/README.md +// +// The actual pin mapping depends on the board revision! The default values are +// set as the 40 pins header on Raspberry Pi 2 and Raspberry Pi 3. +// +// Some header info here: http://elinux.org/RPi_Low-level_peripherals +// +// P1 is also known as J8 on A+, B+, 2 and later. +var ( + // Raspberry Pi A and B, 26 pin header: + P1_1 pin.Pin = pin.V3_3 // max 30mA + P1_2 pin.Pin = pin.V5 // (filtered) + P1_3 gpio.PinIO = bcm283x.GPIO2 // High, I2C1_SDA + P1_4 pin.Pin = pin.V5 // + P1_5 gpio.PinIO = bcm283x.GPIO3 // High, I2C1_SCL + P1_6 pin.Pin = pin.GROUND // + P1_7 gpio.PinIO = bcm283x.GPIO4 // High, CLK0 + P1_8 gpio.PinIO = bcm283x.GPIO14 // Low, UART0_TX, UART1_TX + P1_9 pin.Pin = pin.GROUND // + P1_10 gpio.PinIO = bcm283x.GPIO15 // Low, UART0_RX, UART1_RX + P1_11 gpio.PinIO = bcm283x.GPIO17 // Low, UART0_RTS, SPI1_CS1, UART1_RTS + P1_12 gpio.PinIO = bcm283x.GPIO18 // Low, I2S_SCK, SPI1_CS0, PWM0 + P1_13 gpio.PinIO = bcm283x.GPIO27 // Low, + P1_14 pin.Pin = pin.GROUND // + P1_15 gpio.PinIO = bcm283x.GPIO22 // Low, + P1_16 gpio.PinIO = bcm283x.GPIO23 // Low, + P1_17 pin.Pin = pin.V3_3 // + P1_18 gpio.PinIO = bcm283x.GPIO24 // Low, + P1_19 gpio.PinIO = bcm283x.GPIO10 // Low, SPI0_MOSI + P1_20 pin.Pin = pin.GROUND // + P1_21 gpio.PinIO = bcm283x.GPIO9 // Low, SPI0_MISO + P1_22 gpio.PinIO = bcm283x.GPIO25 // Low, + P1_23 gpio.PinIO = bcm283x.GPIO11 // Low, SPI0_CLK + P1_24 gpio.PinIO = bcm283x.GPIO8 // High, SPI0_CS0 + P1_25 pin.Pin = pin.GROUND // + P1_26 gpio.PinIO = bcm283x.GPIO7 // High, SPI0_CS1 + + // Raspberry Pi A+, B+, 2 and later, 40 pin header (also named J8): + P1_27 gpio.PinIO = bcm283x.GPIO0 // High, I2C0_SDA used to probe for HAT EEPROM, see https://github.com/raspberrypi/hats + P1_28 gpio.PinIO = bcm283x.GPIO1 // High, I2C0_SCL + P1_29 gpio.PinIO = bcm283x.GPIO5 // High, CLK1 + P1_30 pin.Pin = pin.GROUND // + P1_31 gpio.PinIO = bcm283x.GPIO6 // High, CLK2 + P1_32 gpio.PinIO = bcm283x.GPIO12 // Low, PWM0 + P1_33 gpio.PinIO = bcm283x.GPIO13 // Low, PWM1 + P1_34 pin.Pin = pin.GROUND // + P1_35 gpio.PinIO = bcm283x.GPIO19 // Low, I2S_WS, SPI1_MISO, PWM1 + P1_36 gpio.PinIO = bcm283x.GPIO16 // Low, UART0_CTS, SPI1_CS2, UART1_CTS + P1_37 gpio.PinIO = bcm283x.GPIO26 // + P1_38 gpio.PinIO = bcm283x.GPIO20 // Low, I2S_DIN, SPI1_MOSI, CLK0 + P1_39 pin.Pin = pin.GROUND // + P1_40 gpio.PinIO = bcm283x.GPIO21 // Low, I2S_DOUT, SPI1_CLK, CLK1 + + // P5 header on Raspberry Pi A and B, PCB v2: + P5_1 pin.Pin = pin.V5 + P5_2 pin.Pin = pin.V3_3 + P5_3 gpio.PinIO = bcm283x.GPIO28 // Float, I2C0_SDA, I2S_SCK + P5_4 gpio.PinIO = bcm283x.GPIO29 // Float, I2C0_SCL, I2S_WS + P5_5 gpio.PinIO = bcm283x.GPIO30 // Low, I2S_DIN, UART0_CTS, UART1_CTS + P5_6 gpio.PinIO = bcm283x.GPIO31 // Low, I2S_DOUT, UART0_RTS, UART1_RTS + P5_7 pin.Pin = pin.GROUND + P5_8 pin.Pin = pin.GROUND + + AUDIO_RIGHT = bcm283x.GPIO40 // Low, PWM0, SPI2_MISO, UART1_TX + AUDIO_LEFT = bcm283x.GPIO41 // Low, PWM1, SPI2_MOSI, UART1_RX + HDMI_HOTPLUG_DETECT = bcm283x.GPIO46 // High, +) + +// Pin as connected on the SODIMM header. +// +// Documentation is https://www.raspberrypi.org/documentation/hardware/computemodule/datasheets/rpi_DATA_CM_1p0.pdf +// +// There are some differences for CM3-Lite and CM1. +var ( + SO_1 pin.Pin = pin.GROUND // GND + SO_2 pin.Pin = pin.INVALID // EMMC_DISABLE_N + SO_3 gpio.PinIO = bcm283x.GPIO0 // GPIO0 + SO_4 pin.Pin = pin.INVALID // NC, SDX_VDD, NC + SO_5 gpio.PinIO = bcm283x.GPIO1 // GPIO1 + SO_6 pin.Pin = pin.INVALID // NC, SDX_VDD, NC + SO_7 pin.Pin = pin.GROUND // GND + SO_8 pin.Pin = pin.GROUND // GND + SO_9 gpio.PinIO = bcm283x.GPIO2 // GPIO2 + SO_10 pin.Pin = pin.INVALID // NC, SDX_CLK, NC + SO_11 gpio.PinIO = bcm283x.GPIO3 // GPIO3 + SO_12 pin.Pin = pin.INVALID // NC, SDX_CMD, NC + SO_13 pin.Pin = pin.GROUND // GND + SO_14 pin.Pin = pin.GROUND // GND + SO_15 gpio.PinIO = bcm283x.GPIO4 // GPIO4 + SO_16 pin.Pin = pin.INVALID // NC, SDX_D0, NC + SO_17 gpio.PinIO = bcm283x.GPIO5 // GPIO5 + SO_18 pin.Pin = pin.INVALID // NC, SDX_D1, NC + SO_19 pin.Pin = pin.GROUND // GND + SO_20 pin.Pin = pin.GROUND // GND + SO_21 gpio.PinIO = bcm283x.GPIO6 // GPIO6 + SO_22 pin.Pin = pin.INVALID // NC, SDX_D2, NC + SO_23 gpio.PinIO = bcm283x.GPIO7 // GPIO7 + SO_24 pin.Pin = pin.INVALID // NC, SDX_D3, NC + SO_25 pin.Pin = pin.GROUND // GND + SO_26 pin.Pin = pin.GROUND // GND + SO_27 gpio.PinIO = bcm283x.GPIO8 // GPIO8 + SO_28 gpio.PinIO = bcm283x.GPIO28 // GPIO28 + SO_29 gpio.PinIO = bcm283x.GPIO9 // GPIO9 + SO_30 gpio.PinIO = bcm283x.GPIO29 // GPIO29 + SO_31 pin.Pin = pin.GROUND // GND + SO_32 pin.Pin = pin.GROUND // GND + SO_33 gpio.PinIO = bcm283x.GPIO10 // GPIO10 + SO_34 gpio.PinIO = bcm283x.GPIO30 // GPIO30 + SO_35 gpio.PinIO = bcm283x.GPIO11 // GPIO11 + SO_36 gpio.PinIO = bcm283x.GPIO31 // GPIO31 + SO_37 pin.Pin = pin.GROUND // GND + SO_38 pin.Pin = pin.GROUND // GND + SO_39 pin.Pin = pin.DC_IN // GPIO0-27_VDD + SO_40 pin.Pin = pin.DC_IN // GPIO0-27_VDD + SO_41 pin.Pin = pin.DC_IN // GPIO28-45_VDD + SO_42 pin.Pin = pin.DC_IN // GPIO28-45_VDD + SO_43 pin.Pin = pin.GROUND // GND + SO_44 pin.Pin = pin.GROUND // GND + SO_45 gpio.PinIO = bcm283x.GPIO12 // GPIO12 + SO_46 gpio.PinIO = bcm283x.GPIO32 // GPIO32 + SO_47 gpio.PinIO = bcm283x.GPIO13 // GPIO13 + SO_48 gpio.PinIO = bcm283x.GPIO33 // GPIO33 + SO_49 pin.Pin = pin.GROUND // GND + SO_50 pin.Pin = pin.GROUND // GND + SO_51 gpio.PinIO = bcm283x.GPIO14 // GPIO14 + SO_52 gpio.PinIO = bcm283x.GPIO34 // GPIO34 + SO_53 gpio.PinIO = bcm283x.GPIO15 // GPIO15 + SO_54 gpio.PinIO = bcm283x.GPIO35 // GPIO35 + SO_55 pin.Pin = pin.GROUND // GND + SO_56 pin.Pin = pin.GROUND // GND + SO_57 gpio.PinIO = bcm283x.GPIO16 // GPIO16 + SO_58 gpio.PinIO = bcm283x.GPIO36 // GPIO36 + SO_59 gpio.PinIO = bcm283x.GPIO17 // GPIO17 + SO_60 gpio.PinIO = bcm283x.GPIO37 // GPIO37 + SO_61 pin.Pin = pin.GROUND // GND + SO_62 pin.Pin = pin.GROUND // GND + SO_63 gpio.PinIO = bcm283x.GPIO18 // GPIO18 + SO_64 gpio.PinIO = bcm283x.GPIO38 // GPIO38 + SO_65 gpio.PinIO = bcm283x.GPIO19 // GPIO19 + SO_66 gpio.PinIO = bcm283x.GPIO39 // GPIO39 + SO_67 pin.Pin = pin.GROUND // GND + SO_68 pin.Pin = pin.GROUND // GND + SO_69 gpio.PinIO = bcm283x.GPIO20 // GPIO20 + SO_70 gpio.PinIO = bcm283x.GPIO40 // GPIO40 + SO_71 gpio.PinIO = bcm283x.GPIO21 // GPIO21 + SO_72 gpio.PinIO = bcm283x.GPIO41 // GPIO41 + SO_73 pin.Pin = pin.GROUND // GND + SO_74 pin.Pin = pin.GROUND // GND + SO_75 gpio.PinIO = bcm283x.GPIO22 // GPIO22 + SO_76 gpio.PinIO = bcm283x.GPIO42 // GPIO42 + SO_77 gpio.PinIO = bcm283x.GPIO23 // GPIO23 + SO_78 gpio.PinIO = bcm283x.GPIO43 // GPIO43 + SO_79 pin.Pin = pin.GROUND // GND + SO_80 pin.Pin = pin.GROUND // GND + SO_81 gpio.PinIO = bcm283x.GPIO24 // GPIO24 + SO_82 gpio.PinIO = bcm283x.GPIO44 // GPIO44 + SO_83 gpio.PinIO = bcm283x.GPIO25 // GPIO25 + SO_84 gpio.PinIO = bcm283x.GPIO45 // GPIO45 + SO_85 pin.Pin = pin.GROUND // GND + SO_86 pin.Pin = pin.GROUND // GND + SO_87 gpio.PinIO = bcm283x.GPIO26 // GPIO26 + SO_88 pin.Pin = pin.INVALID // HDMI_HPD_N_1V8, HDMI_HPD_N_1V8, GPIO46_1V8 + SO_89 gpio.PinIO = bcm283x.GPIO27 // GPIO27 + SO_90 pin.Pin = pin.INVALID // EMMC_EN_N_1V8, EMMC_EN_N_1V8, GPIO47_1V8 + SO_91 pin.Pin = pin.GROUND // GND + SO_92 pin.Pin = pin.GROUND // GND + SO_93 pin.Pin = pin.INVALID // DSI0_DN1 + SO_94 pin.Pin = pin.INVALID // DSI1_DP0 + SO_95 pin.Pin = pin.INVALID // DSI0_DP1 + SO_96 pin.Pin = pin.INVALID // DSI1_DN0 + SO_97 pin.Pin = pin.GROUND // GND + SO_98 pin.Pin = pin.GROUND // GND + SO_99 pin.Pin = pin.INVALID // DSI0_DN0 + SO_100 pin.Pin = pin.INVALID // DSI1_CP + SO_101 pin.Pin = pin.INVALID // DSI0_DP0 + SO_102 pin.Pin = pin.INVALID // DSI1_CN + SO_103 pin.Pin = pin.GROUND // GND + SO_104 pin.Pin = pin.GROUND // GND + SO_105 pin.Pin = pin.INVALID // DSI0_CN + SO_106 pin.Pin = pin.INVALID // DSI1_DP3 + SO_107 pin.Pin = pin.INVALID // DSI0_CP + SO_108 pin.Pin = pin.INVALID // DSI1_DN3 + SO_109 pin.Pin = pin.GROUND // GND + SO_110 pin.Pin = pin.GROUND // GND + SO_111 pin.Pin = pin.INVALID // HDMI_CLK_N + SO_112 pin.Pin = pin.INVALID // DSI1_DP2 + SO_113 pin.Pin = pin.INVALID // HDMI_CLK_P + SO_114 pin.Pin = pin.INVALID // DSI1_DN2 + SO_115 pin.Pin = pin.GROUND // GND + SO_116 pin.Pin = pin.GROUND // GND + SO_117 pin.Pin = pin.INVALID // HDMI_D0_N + SO_118 pin.Pin = pin.INVALID // DSI1_DP1 + SO_119 pin.Pin = pin.INVALID // HDMI_D0_P + SO_120 pin.Pin = pin.INVALID // DSI1_DN1 + SO_121 pin.Pin = pin.GROUND // GND + SO_122 pin.Pin = pin.GROUND // GND + SO_123 pin.Pin = pin.INVALID // HDMI_D1_N + SO_124 pin.Pin = pin.INVALID // NC + SO_125 pin.Pin = pin.INVALID // HDMI_D1_P + SO_126 pin.Pin = pin.INVALID // NC + SO_127 pin.Pin = pin.GROUND // GND + SO_128 pin.Pin = pin.INVALID // NC + SO_129 pin.Pin = pin.INVALID // HDMI_D2_N + SO_130 pin.Pin = pin.INVALID // NC + SO_131 pin.Pin = pin.INVALID // HDMI_D2_P + SO_132 pin.Pin = pin.INVALID // NC + SO_133 pin.Pin = pin.GROUND // GND + SO_134 pin.Pin = pin.GROUND // GND + SO_135 pin.Pin = pin.INVALID // CAM1_DP3 + SO_136 pin.Pin = pin.INVALID // CAM0_DP0 + SO_137 pin.Pin = pin.INVALID // CAM1_DN3 + SO_138 pin.Pin = pin.INVALID // CAM0_DN0 + SO_139 pin.Pin = pin.GROUND // GND + SO_140 pin.Pin = pin.GROUND // GND + SO_141 pin.Pin = pin.INVALID // CAM1_DP2 + SO_142 pin.Pin = pin.INVALID // CAM0_CP + SO_143 pin.Pin = pin.INVALID // CAM1_DN2 + SO_144 pin.Pin = pin.INVALID // CAM0_CN + SO_145 pin.Pin = pin.GROUND // GND + SO_146 pin.Pin = pin.GROUND // GND + SO_147 pin.Pin = pin.INVALID // CAM1_CP + SO_148 pin.Pin = pin.INVALID // CAM0_DP1 + SO_149 pin.Pin = pin.INVALID // CAM1_CN + SO_150 pin.Pin = pin.INVALID // CAM0_DN1 + SO_151 pin.Pin = pin.GROUND // GND + SO_152 pin.Pin = pin.GROUND // GND + SO_153 pin.Pin = pin.INVALID // CAM1_DP1 + SO_154 pin.Pin = pin.INVALID // NC + SO_155 pin.Pin = pin.INVALID // CAM1_DN1 + SO_156 pin.Pin = pin.INVALID // NC + SO_157 pin.Pin = pin.GROUND // GND + SO_158 pin.Pin = pin.INVALID // NC + SO_159 pin.Pin = pin.INVALID // CAM1_DP0 + SO_160 pin.Pin = pin.INVALID // NC + SO_161 pin.Pin = pin.INVALID // CAM1_DN0 + SO_162 pin.Pin = pin.INVALID // NC + SO_163 pin.Pin = pin.GROUND // GND + SO_164 pin.Pin = pin.GROUND // GND + SO_165 pin.Pin = pin.INVALID // USB_DP + SO_166 pin.Pin = pin.INVALID // TVDAC + SO_167 pin.Pin = pin.INVALID // USB_DM + SO_168 pin.Pin = pin.INVALID // USB_OTGID + SO_169 pin.Pin = pin.GROUND // GND + SO_170 pin.Pin = pin.GROUND // GND + SO_171 pin.Pin = pin.INVALID // HDMI_CEC + SO_172 pin.Pin = pin.INVALID // VC_TRST_N + SO_173 pin.Pin = pin.INVALID // HDMI_SDA + SO_174 pin.Pin = pin.INVALID // VC_TDI + SO_175 pin.Pin = pin.INVALID // HDMI_SCL + SO_176 pin.Pin = pin.INVALID // VC_TMS + SO_177 pin.Pin = pin.INVALID // RUN + SO_178 pin.Pin = pin.INVALID // VC_TDO + SO_179 pin.Pin = pin.INVALID // VDD_CORE (DO NOT CONNECT) + SO_180 pin.Pin = pin.INVALID // VC_TCK + SO_181 pin.Pin = pin.GROUND // GND + SO_182 pin.Pin = pin.GROUND // GND + SO_183 pin.Pin = pin.V1_8 // 1V8 + SO_184 pin.Pin = pin.V1_8 // 1V8 + SO_185 pin.Pin = pin.V1_8 // 1V8 + SO_186 pin.Pin = pin.V1_8 // 1V8 + SO_187 pin.Pin = pin.GROUND // GND + SO_188 pin.Pin = pin.GROUND // GND + SO_189 pin.Pin = pin.DC_IN // VDAC + SO_190 pin.Pin = pin.DC_IN // VDAC + SO_191 pin.Pin = pin.V3_3 // 3V3 + SO_192 pin.Pin = pin.V3_3 // 3V3 + SO_193 pin.Pin = pin.V3_3 // 3V3 + SO_194 pin.Pin = pin.V3_3 // 3V3 + SO_195 pin.Pin = pin.GROUND // GND + SO_196 pin.Pin = pin.GROUND // GND + SO_197 pin.Pin = pin.DC_IN // VBAT + SO_198 pin.Pin = pin.DC_IN // VBAT + SO_199 pin.Pin = pin.DC_IN // VBAT + SO_200 pin.Pin = pin.DC_IN // VBAT +) + +// driver implements periph.Driver. +type driver struct { +} + +func (d *driver) String() string { + return "rpi" +} + +func (d *driver) Prerequisites() []string { + return nil +} + +func (d *driver) After() []string { + return []string{"bcm283x-gpio"} +} + +func (d *driver) Init() (bool, error) { + if !Present() { + return false, errors.New("Raspberry Pi board not detected") + } + + // Setup headers based on board revision. + // + // This code is not futureproof, it will error out on a Raspberry Pi 4 + // whenever it comes out. + // Revision codes from: http://elinux.org/RPi_HardwareHistory + has26PinP1Header := false + has40PinP1Header := false + hasP5Header := false + hasAudio := false + hasNewAudio := false + hasHDMI := false + hasSODimm := false + rev := distro.CPUInfo()["Revision"] + if i, err := strconv.ParseInt(rev, 16, 32); err == nil { + // Ignore the overclock bit. + i &= 0xFFFFFF + switch i { + case 0x0002, 0x0003: // B v1.0 + has26PinP1Header = true + hasAudio = true + case 0x0004, 0x0005, 0x0006, // B v2.0 + 0x0007, 0x0008, 0x0009, // A v2.0 + 0x000d, 0x000e, 0x000f: // B v2.0 + has26PinP1Header = true + // Only the v2 PCB has the P5 header. + hasP5Header = true + hasAudio = true + hasHDMI = true + case 0x0010, // B+ v1.0 + 0x0012, // A+ v1.1 + 0x0013, // B+ v1.2 + 0x0015, // A+ v1.1 + 0x90021, // A+ v1.1 + 0x90032, // B+ v1.2 + 0xa01040, // 2 Model B v1.0 + 0xa01041, 0xa21041, // 2 Model B v1.1 + 0xa22042: // 2 Model B v1.2 + has40PinP1Header = true + hasAudio = true + hasHDMI = true + case 0x900092, // Zero v1.2 + 0x900093, // Zero v1.3 + 0x920093, // Zero v1.3 + 0x9000c1: // Zero W v1.1 + has40PinP1Header = true + hasHDMI = true + case 0x0011, // Compute Module 1 + 0x0014: // Compute Module 1 + // SODIMM not defined + case 0xa020a0: // Compute Module 3 v1.0 + hasSODimm = true + // tell CM3 and CM3-Lite apart, if possible + case 0xa02082, 0xa22082, 0xa32082, 0xa020d3: // 3 Model B v1.2, B+ + has40PinP1Header = true + hasAudio = true + hasNewAudio = true + hasHDMI = true + default: + return true, fmt.Errorf("rpi: unknown hardware version: 0x%x", i) + } + } else { + return true, fmt.Errorf("rpi: failed to read cpu_info: %v", err) + } + + if has26PinP1Header { + if err := pinreg.Register("P1", [][]pin.Pin{ + {P1_1, P1_2}, + {P1_3, P1_4}, + {P1_5, P1_6}, + {P1_7, P1_8}, + {P1_9, P1_10}, + {P1_11, P1_12}, + {P1_13, P1_14}, + {P1_15, P1_16}, + {P1_17, P1_18}, + {P1_19, P1_20}, + {P1_21, P1_22}, + {P1_23, P1_24}, + {P1_25, P1_26}, + }); err != nil { + return true, err + } + + // TODO(maruel): Models from 2012 and earlier have P1_3=GPIO0, P1_5=GPIO1 and P1_13=GPIO21. + // P2 and P3 are not useful. + // P6 has a RUN pin for reset but it's not available after Pi version 1. + P1_27 = gpio.INVALID + P1_28 = gpio.INVALID + P1_29 = gpio.INVALID + P1_30 = pin.INVALID + P1_31 = gpio.INVALID + P1_32 = gpio.INVALID + P1_33 = gpio.INVALID + P1_34 = pin.INVALID + P1_35 = gpio.INVALID + P1_36 = gpio.INVALID + P1_37 = gpio.INVALID + P1_38 = gpio.INVALID + P1_39 = pin.INVALID + P1_40 = gpio.INVALID + } else if has40PinP1Header { + if err := pinreg.Register("P1", [][]pin.Pin{ + {P1_1, P1_2}, + {P1_3, P1_4}, + {P1_5, P1_6}, + {P1_7, P1_8}, + {P1_9, P1_10}, + {P1_11, P1_12}, + {P1_13, P1_14}, + {P1_15, P1_16}, + {P1_17, P1_18}, + {P1_19, P1_20}, + {P1_21, P1_22}, + {P1_23, P1_24}, + {P1_25, P1_26}, + {P1_27, P1_28}, + {P1_29, P1_30}, + {P1_31, P1_32}, + {P1_33, P1_34}, + {P1_35, P1_36}, + {P1_37, P1_38}, + {P1_39, P1_40}, + }); err != nil { + return true, err + } + } else { + P1_1 = pin.INVALID + P1_2 = pin.INVALID + P1_3 = gpio.INVALID + P1_4 = pin.INVALID + P1_5 = gpio.INVALID + P1_6 = pin.INVALID + P1_7 = gpio.INVALID + P1_8 = gpio.INVALID + P1_9 = pin.INVALID + P1_10 = gpio.INVALID + P1_11 = gpio.INVALID + P1_12 = gpio.INVALID + P1_13 = gpio.INVALID + P1_14 = pin.INVALID + P1_15 = gpio.INVALID + P1_16 = gpio.INVALID + P1_17 = pin.INVALID + P1_18 = gpio.INVALID + P1_19 = gpio.INVALID + P1_20 = pin.INVALID + P1_21 = gpio.INVALID + P1_22 = gpio.INVALID + P1_23 = gpio.INVALID + P1_24 = gpio.INVALID + P1_25 = pin.INVALID + P1_26 = gpio.INVALID + P1_27 = gpio.INVALID + P1_28 = gpio.INVALID + P1_29 = gpio.INVALID + P1_30 = pin.INVALID + P1_31 = gpio.INVALID + P1_32 = gpio.INVALID + P1_33 = gpio.INVALID + P1_34 = pin.INVALID + P1_35 = gpio.INVALID + P1_36 = gpio.INVALID + P1_37 = gpio.INVALID + P1_38 = gpio.INVALID + P1_39 = pin.INVALID + P1_40 = gpio.INVALID + } + + // Only the A and B v2 PCB has the P5 header. + if hasP5Header { + if err := pinreg.Register("P5", [][]pin.Pin{ + {P5_1, P5_2}, + {P5_3, P5_4}, + {P5_5, P5_6}, + {P5_7, P5_8}, + }); err != nil { + return true, err + } + } else { + P5_1 = pin.INVALID + P5_2 = pin.INVALID + P5_3 = gpio.INVALID + P5_4 = gpio.INVALID + P5_5 = gpio.INVALID + P5_6 = gpio.INVALID + P5_7 = pin.INVALID + P5_8 = pin.INVALID + } + + if hasSODimm { + if err := pinreg.Register("SO", [][]pin.Pin{ + {SO_1, SO_2}, + {SO_3, SO_4}, + {SO_5, SO_6}, + {SO_7, SO_8}, + {SO_9, SO_10}, + {SO_11, SO_12}, + {SO_13, SO_14}, + {SO_15, SO_16}, + {SO_17, SO_18}, + {SO_19, SO_20}, + {SO_21, SO_22}, + {SO_23, SO_24}, + {SO_25, SO_26}, + {SO_27, SO_28}, + {SO_29, SO_30}, + {SO_31, SO_32}, + {SO_33, SO_34}, + {SO_35, SO_36}, + {SO_37, SO_38}, + {SO_39, SO_40}, + {SO_41, SO_42}, + {SO_43, SO_44}, + {SO_45, SO_46}, + {SO_47, SO_48}, + {SO_49, SO_50}, + {SO_51, SO_52}, + {SO_53, SO_54}, + {SO_55, SO_56}, + {SO_57, SO_58}, + {SO_59, SO_60}, + {SO_61, SO_62}, + {SO_63, SO_64}, + {SO_65, SO_66}, + {SO_67, SO_68}, + {SO_69, SO_70}, + {SO_71, SO_72}, + {SO_73, SO_74}, + {SO_75, SO_76}, + {SO_77, SO_78}, + {SO_79, SO_80}, + {SO_81, SO_82}, + {SO_83, SO_84}, + {SO_85, SO_86}, + {SO_87, SO_88}, + {SO_89, SO_90}, + {SO_91, SO_92}, + {SO_93, SO_94}, + {SO_95, SO_96}, + {SO_97, SO_98}, + {SO_99, SO_100}, + {SO_101, SO_102}, + {SO_103, SO_104}, + {SO_105, SO_106}, + {SO_107, SO_108}, + {SO_109, SO_110}, + {SO_111, SO_112}, + {SO_113, SO_114}, + {SO_115, SO_116}, + {SO_117, SO_118}, + {SO_119, SO_120}, + {SO_121, SO_122}, + {SO_123, SO_124}, + {SO_125, SO_126}, + {SO_127, SO_128}, + {SO_129, SO_130}, + {SO_131, SO_132}, + {SO_133, SO_134}, + {SO_135, SO_136}, + {SO_137, SO_138}, + {SO_139, SO_140}, + {SO_141, SO_142}, + {SO_143, SO_144}, + {SO_145, SO_146}, + {SO_147, SO_148}, + {SO_149, SO_150}, + {SO_151, SO_152}, + {SO_153, SO_154}, + {SO_155, SO_156}, + {SO_157, SO_158}, + {SO_159, SO_160}, + {SO_161, SO_162}, + {SO_163, SO_164}, + {SO_165, SO_166}, + {SO_167, SO_168}, + {SO_169, SO_170}, + {SO_171, SO_172}, + {SO_173, SO_174}, + {SO_175, SO_176}, + {SO_177, SO_178}, + {SO_179, SO_180}, + {SO_181, SO_182}, + {SO_183, SO_184}, + {SO_185, SO_186}, + {SO_187, SO_188}, + {SO_189, SO_190}, + {SO_191, SO_192}, + {SO_193, SO_194}, + {SO_195, SO_196}, + {SO_197, SO_198}, + {SO_199, SO_200}, + }); err != nil { + return true, err + } + } + + if hasAudio { + if !hasNewAudio { + AUDIO_LEFT = bcm283x.GPIO45 // PWM1 + } + if err := pinreg.Register("AUDIO", [][]pin.Pin{ + {AUDIO_LEFT}, + {AUDIO_RIGHT}, + }); err != nil { + return true, err + } + } + + if hasHDMI { + if err := pinreg.Register("HDMI", [][]pin.Pin{{HDMI_HOTPLUG_DETECT}}); err != nil { + return true, err + } + } + return true, nil +} + +func init() { + if isArm { + periph.MustRegister(&drv) + } +} + +var drv driver diff --git a/vendor/periph.io/x/periph/host/rpi/rpi_arm.go b/vendor/periph.io/x/periph/host/rpi/rpi_arm.go new file mode 100644 index 0000000..acf1d96 --- /dev/null +++ b/vendor/periph.io/x/periph/host/rpi/rpi_arm.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package rpi + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/rpi/rpi_arm64.go b/vendor/periph.io/x/periph/host/rpi/rpi_arm64.go new file mode 100644 index 0000000..ad1347a --- /dev/null +++ b/vendor/periph.io/x/periph/host/rpi/rpi_arm64.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build arm64 + +package rpi + +const isArm = true diff --git a/vendor/periph.io/x/periph/host/rpi/rpi_other.go b/vendor/periph.io/x/periph/host/rpi/rpi_other.go new file mode 100644 index 0000000..414d714 --- /dev/null +++ b/vendor/periph.io/x/periph/host/rpi/rpi_other.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !arm,!arm64 + +package rpi + +const isArm = false diff --git a/vendor/periph.io/x/periph/host/sysfs/doc.go b/vendor/periph.io/x/periph/host/sysfs/doc.go new file mode 100644 index 0000000..87eb973 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/doc.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package sysfs implements a sane library to interact with sysfs provided +// hardware access. +// +// sysfs a virtual file system rooted at /sys/. +// +// This package also include drivers using devfs. +// +// https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt +package sysfs diff --git a/vendor/periph.io/x/periph/host/sysfs/gpio.go b/vendor/periph.io/x/periph/host/sysfs/gpio.go new file mode 100644 index 0000000..eecdac0 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/gpio.go @@ -0,0 +1,515 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package sysfs + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "sync" + "time" + + "periph.io/x/periph" + "periph.io/x/periph/conn" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/fs" +) + +// Pins is all the pins exported by GPIO sysfs. +// +// Some CPU architectures have the pin numbers start at 0 and use consecutive +// pin numbers but this is not the case for all CPU architectures, some +// have gaps in the pin numbering. +// +// This global variable is initialized once at driver initialization and isn't +// mutated afterward. Do not modify it. +var Pins map[int]*Pin + +// Pin represents one GPIO pin as found by sysfs. +type Pin struct { + number int + name string + root string // Something like /sys/class/gpio/gpio%d/ + + mu sync.Mutex + err error // If open() failed + direction direction // Cache of the last known direction + edge gpio.Edge // Cache of the last edge used. + fDirection fileIO // handle to /sys/class/gpio/gpio*/direction; never closed + fEdge fileIO // handle to /sys/class/gpio/gpio*/edge; never closed + fValue fileIO // handle to /sys/class/gpio/gpio*/value; never closed + event fs.Event // Initialized once + buf [4]byte // scratch buffer for Function(), Read() and Out() +} + +// String implements conn.Resource. +func (p *Pin) String() string { + return p.name +} + +// Halt implements conn.Resource. +// +// It stops edge detection if enabled. +func (p *Pin) Halt() error { + p.mu.Lock() + defer p.mu.Unlock() + return p.haltEdge() +} + +// Name implements pin.Pin. +func (p *Pin) Name() string { + return p.name +} + +// Number implements pin.Pin. +func (p *Pin) Number() int { + return p.number +} + +// Function implements pin.Pin. +func (p *Pin) Function() string { + return string(p.Func()) +} + +// Func implements pin.PinFunc. +func (p *Pin) Func() pin.Func { + p.mu.Lock() + defer p.mu.Unlock() + // TODO(maruel): There's an internal bug which causes p.direction to be + // invalid (!?) Need to figure it out ASAP. + if err := p.open(); err != nil { + return pin.Func("ERR") + } + if _, err := seekRead(p.fDirection, p.buf[:]); err != nil { + return pin.Func("ERR") + } + if p.buf[0] == 'i' && p.buf[1] == 'n' { + p.direction = dIn + } else if p.buf[0] == 'o' && p.buf[1] == 'u' && p.buf[2] == 't' { + p.direction = dOut + } + if p.direction == dIn { + if p.Read() { + return gpio.IN_HIGH + } + return gpio.IN_LOW + } else if p.direction == dOut { + if p.Read() { + return gpio.OUT_HIGH + } + return gpio.OUT_LOW + } + return pin.Func("ERR") +} + +// SupportedFuncs implements pin.PinFunc. +func (p *Pin) SupportedFuncs() []pin.Func { + return []pin.Func{gpio.IN, gpio.OUT} +} + +// SetFunc implements pin.PinFunc. +func (p *Pin) SetFunc(f pin.Func) error { + switch f { + case gpio.IN: + return p.In(gpio.PullNoChange, gpio.NoEdge) + case gpio.OUT_HIGH: + return p.Out(gpio.High) + case gpio.OUT, gpio.OUT_LOW: + return p.Out(gpio.Low) + default: + return p.wrap(errors.New("unsupported function")) + } +} + +// In implements gpio.PinIn. +func (p *Pin) In(pull gpio.Pull, edge gpio.Edge) error { + if pull != gpio.PullNoChange && pull != gpio.Float { + return p.wrap(errors.New("doesn't support pull-up/pull-down")) + } + p.mu.Lock() + defer p.mu.Unlock() + if p.direction != dIn { + if err := p.open(); err != nil { + return p.wrap(err) + } + if err := seekWrite(p.fDirection, bIn); err != nil { + return p.wrap(err) + } + p.direction = dIn + } + // Always push none to help accumulated flush edges. This is not fool proof + // but it seems to help. + if p.fEdge != nil { + if err := seekWrite(p.fEdge, bNone); err != nil { + return p.wrap(err) + } + } + // Assume that when the pin was switched, the driver doesn't recall if edge + // triggering was enabled. + if edge != gpio.NoEdge { + if p.fEdge == nil { + var err error + p.fEdge, err = fileIOOpen(p.root+"edge", os.O_RDWR) + if err != nil { + return p.wrap(err) + } + if err = p.event.MakeEvent(p.fValue.Fd()); err != nil { + _ = p.fEdge.Close() + p.fEdge = nil + return p.wrap(err) + } + } + // Always reset the edge detection mode to none after starting the epoll + // otherwise edges are not always delivered, as observed on an Allwinner A20 + // running kernel 4.14.14. + if err := seekWrite(p.fEdge, bNone); err != nil { + return p.wrap(err) + } + var b []byte + switch edge { + case gpio.RisingEdge: + b = bRising + case gpio.FallingEdge: + b = bFalling + case gpio.BothEdges: + b = bBoth + } + if err := seekWrite(p.fEdge, b); err != nil { + return p.wrap(err) + } + } + p.edge = edge + // This helps to remove accumulated edges but this is not 100% sufficient. + // Most of the time the interrupts are handled promptly enough that this loop + // flushes the accumulated interrupt. + // Sometimes the kernel may have accumulated interrupts that haven't been + // processed for a long time, it can easily be >300µs even on a quite idle + // CPU. In this case, the loop below is not sufficient, since the interrupt + // will happen afterward "out of the blue". + if edge != gpio.NoEdge { + p.WaitForEdge(0) + } + return nil +} + +// Read implements gpio.PinIn. +func (p *Pin) Read() gpio.Level { + // There's no lock here. + if p.fValue == nil { + return gpio.Low + } + if _, err := seekRead(p.fValue, p.buf[:]); err != nil { + // Error. + return gpio.Low + } + if p.buf[0] == '0' { + return gpio.Low + } + if p.buf[0] == '1' { + return gpio.High + } + // Error. + return gpio.Low +} + +// WaitForEdge implements gpio.PinIn. +func (p *Pin) WaitForEdge(timeout time.Duration) bool { + // Run lockless, as the normal use is to call in a busy loop. + var ms int + if timeout == -1 { + ms = -1 + } else { + ms = int(timeout / time.Millisecond) + } + start := time.Now() + for { + if nr, err := p.event.Wait(ms); err != nil { + return false + } else if nr == 1 { + // TODO(maruel): According to pigpio, the correct way to consume the + // interrupt is to call Seek(). + return true + } + // A signal occurred. + if timeout != -1 { + ms = int((timeout - time.Since(start)) / time.Millisecond) + } + if ms <= 0 { + return false + } + } +} + +// Pull implements gpio.PinIn. +// +// It returns gpio.PullNoChange since gpio sysfs has no support for input pull +// resistor. +func (p *Pin) Pull() gpio.Pull { + return gpio.PullNoChange +} + +// DefaultPull implements gpio.PinIn. +// +// It returns gpio.PullNoChange since gpio sysfs has no support for input pull +// resistor. +func (p *Pin) DefaultPull() gpio.Pull { + return gpio.PullNoChange +} + +// Out implements gpio.PinOut. +func (p *Pin) Out(l gpio.Level) error { + p.mu.Lock() + defer p.mu.Unlock() + if p.direction != dOut { + if err := p.open(); err != nil { + return p.wrap(err) + } + if err := p.haltEdge(); err != nil { + return err + } + // "To ensure glitch free operation, values "low" and "high" may be written + // to configure the GPIO as an output with that initial value." + var d []byte + if l == gpio.Low { + d = bLow + } else { + d = bHigh + } + if err := seekWrite(p.fDirection, d); err != nil { + return p.wrap(err) + } + p.direction = dOut + return nil + } + if l == gpio.Low { + p.buf[0] = '0' + } else { + p.buf[0] = '1' + } + if err := seekWrite(p.fValue, p.buf[:1]); err != nil { + return p.wrap(err) + } + return nil +} + +// PWM implements gpio.PinOut. +// +// This is not supported on sysfs. +func (p *Pin) PWM(gpio.Duty, physic.Frequency) error { + return p.wrap(errors.New("pwm is not supported via sysfs")) +} + +// + +// open opens the gpio sysfs handle to /value and /direction. +// +// lock must be held. +func (p *Pin) open() error { + if p.fDirection != nil || p.err != nil { + return p.err + } + + if drvGPIO.exportHandle == nil { + return errors.New("sysfs gpio is not initialized") + } + var err error + _, err = drvGPIO.exportHandle.Write([]byte(strconv.Itoa(p.number))) + if err != nil && !isErrBusy(err) { + p.err = err + if os.IsPermission(p.err) { + return fmt.Errorf("need more access, try as root or setup udev rules: %v", p.err) + } + return p.err + } + + // There's a race condition where the file may be created but udev is still + // running the Raspbian udev rule to make it readable to the current user. + // It's simpler to just loop a little as if /export is accessible, it doesn't + // make sense that gpioN/value doesn't become accessible eventually. + timeout := 5 * time.Second + for start := time.Now(); time.Since(start) < timeout; { + p.fValue, err = fileIOOpen(p.root+"value", os.O_RDWR) + // The virtual file creation is synchronous when writing to /export for + // udev rule execution is asynchronous. + if err == nil { + break + } + if !os.IsPermission(err) { + p.err = err + break + } + } + if p.err != nil { + return p.err + } + p.fDirection, err = fileIOOpen(p.root+"direction", os.O_RDWR) + if err != nil { + p.err = err + _ = p.fValue.Close() + p.fValue = nil + } + return p.err +} + +// haltEdge stops any on-going edge detection. +func (p *Pin) haltEdge() error { + if p.edge != gpio.NoEdge { + if err := seekWrite(p.fEdge, bNone); err != nil { + return p.wrap(err) + } + p.edge = gpio.NoEdge + // This is still important to remove an accumulated edge. + p.WaitForEdge(0) + } + return nil +} + +func (p *Pin) wrap(err error) error { + return fmt.Errorf("sysfs-gpio (%s): %v", p, err) +} + +// + +type direction int + +const ( + dUnknown direction = 0 + dIn direction = 1 + dOut direction = 2 +) + +var ( + bIn = []byte("in") + bLow = []byte("low") + bHigh = []byte("high") + bNone = []byte("none") + bRising = []byte("rising") + bFalling = []byte("falling") + bBoth = []byte("both") +) + +// readInt reads a pseudo-file (sysfs) that is known to contain an integer and +// returns the parsed number. +func readInt(path string) (int, error) { + f, err := fileIOOpen(path, os.O_RDONLY) + if err != nil { + return 0, err + } + defer f.Close() + var b [24]byte + n, err := f.Read(b[:]) + if err != nil { + return 0, err + } + raw := b[:n] + if len(raw) == 0 || raw[len(raw)-1] != '\n' { + return 0, errors.New("invalid value") + } + return strconv.Atoi(string(raw[:len(raw)-1])) +} + +// driverGPIO implements periph.Driver. +type driverGPIO struct { + exportHandle io.Writer // handle to /sys/class/gpio/export +} + +func (d *driverGPIO) String() string { + return "sysfs-gpio" +} + +func (d *driverGPIO) Prerequisites() []string { + return nil +} + +func (d *driverGPIO) After() []string { + return nil +} + +// Init initializes GPIO sysfs handling code. +// +// Uses gpio sysfs as described at +// https://www.kernel.org/doc/Documentation/gpio/sysfs.txt +// +// GPIO sysfs is often the only way to do edge triggered interrupts. Doing this +// requires cooperation from a driver in the kernel. +// +// The main drawback of GPIO sysfs is that it doesn't expose internal pull +// resistor and it is much slower than using memory mapped hardware registers. +func (d *driverGPIO) Init() (bool, error) { + items, err := filepath.Glob("/sys/class/gpio/gpiochip*") + if err != nil { + return true, err + } + if len(items) == 0 { + return false, errors.New("no GPIO pin found") + } + + // There are hosts that use non-continuous pin numbering so use a map instead + // of an array. + Pins = map[int]*Pin{} + for _, item := range items { + if err := d.parseGPIOChip(item + "/"); err != nil { + return true, err + } + } + drvGPIO.exportHandle, err = fileIOOpen("/sys/class/gpio/export", os.O_WRONLY) + if os.IsPermission(err) { + return true, fmt.Errorf("need more access, try as root or setup udev rules: %v", err) + } + return true, err +} + +func (d *driverGPIO) parseGPIOChip(path string) error { + base, err := readInt(path + "base") + if err != nil { + return err + } + number, err := readInt(path + "ngpio") + if err != nil { + return err + } + // TODO(maruel): The chip driver may lie and lists GPIO pins that cannot be + // exported. The only way to know about it is to export it before opening. + for i := base; i < base+number; i++ { + if _, ok := Pins[i]; ok { + return fmt.Errorf("found two pins with number %d", i) + } + p := &Pin{ + number: i, + name: fmt.Sprintf("GPIO%d", i), + root: fmt.Sprintf("/sys/class/gpio/gpio%d/", i), + } + Pins[i] = p + if err := gpioreg.Register(p); err != nil { + return err + } + // If there is a CPU memory mapped gpio pin with the same number, the + // driver has to unregister this pin and map its own after. + if err := gpioreg.RegisterAlias(strconv.Itoa(i), p.name); err != nil { + return err + } + } + return nil +} + +func init() { + if isLinux { + periph.MustRegister(&drvGPIO) + } +} + +var drvGPIO driverGPIO + +var _ conn.Resource = &Pin{} +var _ gpio.PinIn = &Pin{} +var _ gpio.PinOut = &Pin{} +var _ gpio.PinIO = &Pin{} +var _ pin.PinFunc = &Pin{} diff --git a/vendor/periph.io/x/periph/host/sysfs/i2c.go b/vendor/periph.io/x/periph/host/sysfs/i2c.go new file mode 100644 index 0000000..2112793 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/i2c.go @@ -0,0 +1,388 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package sysfs + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "unsafe" + + "periph.io/x/periph" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/i2c" + "periph.io/x/periph/conn/i2c/i2creg" + "periph.io/x/periph/conn/physic" +) + +// I2CSetSpeedHook can be set by a driver to enable changing the I²C buses +// speed. +func I2CSetSpeedHook(h func(f physic.Frequency) error) error { + if h == nil { + return errors.New("sysfs-i2c: hook must not be nil") + } + drvI2C.mu.Lock() + defer drvI2C.mu.Unlock() + if drvI2C.setSpeed != nil { + return errors.New("sysfs-i2c: a speed hook was already set") + } + drvI2C.setSpeed = h + return nil +} + +// NewI2C opens an I²C bus via its sysfs interface as described at +// https://www.kernel.org/doc/Documentation/i2c/dev-interface. +// +// busNumber is the bus number as exported by sysfs. For example if the path is +// /dev/i2c-1, busNumber should be 1. +// +// The resulting object is safe for concurent use. +// +// Do not use sysfs.NewI2C() directly as the package sysfs is providing a +// https://periph.io/x/periph/conn/i2c Linux-specific implementation. +// +// periph.io works on many OSes! +// +// Instead, use https://periph.io/x/periph/conn/i2c/i2creg#Open. This permits +// it to work on all operating systems, or devices like I²C over USB. +func NewI2C(busNumber int) (*I2C, error) { + if isLinux { + return newI2C(busNumber) + } + return nil, errors.New("sysfs-i2c: is not supported on this platform") +} + +// I2C is an open I²C bus via sysfs. +// +// It can be used to communicate with multiple devices from multiple goroutines. +type I2C struct { + f ioctlCloser + busNumber int + + mu sync.Mutex // In theory the kernel probably has an internal lock but not taking any chance. + fn functionality + scl gpio.PinIO + sda gpio.PinIO +} + +// Close closes the handle to the I²C driver. It is not a requirement to close +// before process termination. +func (i *I2C) Close() error { + i.mu.Lock() + defer i.mu.Unlock() + if err := i.f.Close(); err != nil { + return fmt.Errorf("sysfs-i2c: %v", err) + } + return nil +} + +func (i *I2C) String() string { + return fmt.Sprintf("I2C%d", i.busNumber) +} + +// Tx execute a transaction as a single operation unit. +func (i *I2C) Tx(addr uint16, w, r []byte) error { + if addr >= 0x400 || (addr >= 0x80 && i.fn&func10BitAddr == 0) { + return errors.New("sysfs-i2c: invalid address") + } + if len(w) == 0 && len(r) == 0 { + return nil + } + + // Convert the messages to the internal format. + var buf [2]i2cMsg + msgs := buf[0:0] + if len(w) != 0 { + msgs = buf[:1] + buf[0].addr = addr + buf[0].length = uint16(len(w)) + buf[0].buf = uintptr(unsafe.Pointer(&w[0])) + } + if len(r) != 0 { + l := len(msgs) + msgs = msgs[:l+1] // extend the slice by one + buf[l].addr = addr + buf[l].flags = flagRD + buf[l].length = uint16(len(r)) + buf[l].buf = uintptr(unsafe.Pointer(&r[0])) + } + p := rdwrIoctlData{ + msgs: uintptr(unsafe.Pointer(&msgs[0])), + nmsgs: uint32(len(msgs)), + } + pp := uintptr(unsafe.Pointer(&p)) + i.mu.Lock() + defer i.mu.Unlock() + if err := i.f.Ioctl(ioctlRdwr, pp); err != nil { + return fmt.Errorf("sysfs-i2c: %v", err) + } + return nil +} + +// SetSpeed implements i2c.Bus. +func (i *I2C) SetSpeed(f physic.Frequency) error { + if f > 100*physic.MegaHertz { + return fmt.Errorf("sysfs-i2c: invalid speed %s; maximum supported clock is 100MHz", f) + } + if f < physic.KiloHertz { + return fmt.Errorf("sysfs-i2c: invalid speed %s; minimum supported clock is 1KHz; did you forget to multiply by physic.KiloHertz?", f) + } + drvI2C.mu.Lock() + defer drvI2C.mu.Unlock() + if drvI2C.setSpeed != nil { + return drvI2C.setSpeed(f) + } + return errors.New("sysfs-i2c: not supported") +} + +// SCL implements i2c.Pins. +func (i *I2C) SCL() gpio.PinIO { + i.initPins() + return i.scl +} + +// SDA implements i2c.Pins. +func (i *I2C) SDA() gpio.PinIO { + i.initPins() + return i.sda +} + +// Private details. + +func newI2C(busNumber int) (*I2C, error) { + // Use the devfs path for now instead of sysfs path. + f, err := ioctlOpen(fmt.Sprintf("/dev/i2c-%d", busNumber), os.O_RDWR) + if err != nil { + // Try to be helpful here. There are generally two cases: + // - /dev/i2c-X doesn't exist. In this case, /boot/config.txt has to be + // edited to enable I²C then the device must be rebooted. + // - permission denied. In this case, the user has to be added to plugdev. + if os.IsNotExist(err) { + return nil, fmt.Errorf("sysfs-i2c: bus #%d is not configured: %v", busNumber, err) + } + // TODO(maruel): This is a debianism. + return nil, fmt.Errorf("sysfs-i2c: are you member of group 'plugdev'? %v", err) + } + i := &I2C{f: f, busNumber: busNumber} + + // TODO(maruel): Changing the speed is currently doing this for all devices. + // https://github.com/raspberrypi/linux/issues/215 + // Need to access /sys/module/i2c_bcm2708/parameters/baudrate + + // Query to know if 10 bits addresses are supported. + if err = i.f.Ioctl(ioctlFuncs, uintptr(unsafe.Pointer(&i.fn))); err != nil { + return nil, fmt.Errorf("sysfs-i2c: %v", err) + } + return i, nil +} + +func (i *I2C) initPins() { + i.mu.Lock() + if i.scl == nil { + if i.scl = gpioreg.ByName(fmt.Sprintf("I2C%d_SCL", i.busNumber)); i.scl == nil { + i.scl = gpio.INVALID + } + if i.sda = gpioreg.ByName(fmt.Sprintf("I2C%d_SDA", i.busNumber)); i.sda == nil { + i.sda = gpio.INVALID + } + } + i.mu.Unlock() +} + +// i2cdev driver IOCTL control codes. +// +// Constants and structure definition can be found at +// /usr/include/linux/i2c-dev.h and /usr/include/linux/i2c.h. +const ( + ioctlRetries = 0x701 // TODO(maruel): Expose this + ioctlTimeout = 0x702 // TODO(maruel): Expose this; in units of 10ms + ioctlSlave = 0x703 + ioctlTenBits = 0x704 // TODO(maruel): Expose this but the header says it's broken (!?) + ioctlFuncs = 0x705 + ioctlRdwr = 0x707 +) + +// flags +const ( + flagTEN = 0x0010 // this is a ten bit chip address + flagRD = 0x0001 // read data, from slave to master + flagSTOP = 0x8000 // if funcProtocolMangling + flagNOSTART = 0x4000 // if I2C_FUNC_NOSTART + flagRevDirAddr = 0x2000 // if funcProtocolMangling + flagIgnoreNAK = 0x1000 // if funcProtocolMangling + flagNoRDACK = 0x0800 // if funcProtocolMangling + flagRecvLen = 0x0400 // length will be first received byte + +) + +type functionality uint64 + +const ( + funcI2C = 0x00000001 + func10BitAddr = 0x00000002 + funcProtocolMangling = 0x00000004 // I2C_M_IGNORE_NAK etc. + funcSMBusPEC = 0x00000008 + funcNOSTART = 0x00000010 // I2C_M_NOSTART + funcSMBusBlockProcCall = 0x00008000 // SMBus 2.0 + funcSMBusQuick = 0x00010000 + funcSMBusReadByte = 0x00020000 + funcSMBusWriteByte = 0x00040000 + funcSMBusReadByteData = 0x00080000 + funcSMBusWriteByteData = 0x00100000 + funcSMBusReadWordData = 0x00200000 + funcSMBusWriteWordData = 0x00400000 + funcSMBusProcCall = 0x00800000 + funcSMBusReadBlockData = 0x01000000 + funcSMBusWriteBlockData = 0x02000000 + funcSMBusReadI2CBlock = 0x04000000 // I2C-like block xfer + funcSMBusWriteI2CBlock = 0x08000000 // w/ 1-byte reg. addr. +) + +func (f functionality) String() string { + var out []string + if f&funcI2C != 0 { + out = append(out, "I2C") + } + if f&func10BitAddr != 0 { + out = append(out, "10BIT_ADDR") + } + if f&funcProtocolMangling != 0 { + out = append(out, "PROTOCOL_MANGLING") + } + if f&funcSMBusPEC != 0 { + out = append(out, "SMBUS_PEC") + } + if f&funcNOSTART != 0 { + out = append(out, "NOSTART") + } + if f&funcSMBusBlockProcCall != 0 { + out = append(out, "SMBUS_BLOCK_PROC_CALL") + } + if f&funcSMBusQuick != 0 { + out = append(out, "SMBUS_QUICK") + } + if f&funcSMBusReadByte != 0 { + out = append(out, "SMBUS_READ_BYTE") + } + if f&funcSMBusWriteByte != 0 { + out = append(out, "SMBUS_WRITE_BYTE") + } + if f&funcSMBusReadByteData != 0 { + out = append(out, "SMBUS_READ_BYTE_DATA") + } + if f&funcSMBusWriteByteData != 0 { + out = append(out, "SMBUS_WRITE_BYTE_DATA") + } + if f&funcSMBusReadWordData != 0 { + out = append(out, "SMBUS_READ_WORD_DATA") + } + if f&funcSMBusWriteWordData != 0 { + out = append(out, "SMBUS_WRITE_WORD_DATA") + } + if f&funcSMBusProcCall != 0 { + out = append(out, "SMBUS_PROC_CALL") + } + if f&funcSMBusReadBlockData != 0 { + out = append(out, "SMBUS_READ_BLOCK_DATA") + } + if f&funcSMBusWriteBlockData != 0 { + out = append(out, "SMBUS_WRITE_BLOCK_DATA") + } + if f&funcSMBusReadI2CBlock != 0 { + out = append(out, "SMBUS_READ_I2C_BLOCK") + } + if f&funcSMBusWriteI2CBlock != 0 { + out = append(out, "SMBUS_WRITE_I2C_BLOCK") + } + return strings.Join(out, "|") +} + +type rdwrIoctlData struct { + msgs uintptr // Pointer to i2cMsg + nmsgs uint32 +} + +type i2cMsg struct { + addr uint16 // Address to communicate with + flags uint16 // 1 for read, see i2c.h for more details + length uint16 + buf uintptr +} + +// + +// driverI2C implements periph.Driver. +type driverI2C struct { + mu sync.Mutex + buses []string + setSpeed func(f physic.Frequency) error +} + +func (d *driverI2C) String() string { + return "sysfs-i2c" +} + +func (d *driverI2C) Prerequisites() []string { + return nil +} + +func (d *driverI2C) After() []string { + return nil +} + +func (d *driverI2C) Init() (bool, error) { + // Do not use "/sys/bus/i2c/devices/i2c-" as Raspbian's provided udev rules + // only modify the ACL of /dev/i2c-* but not the ones in /sys/bus/... + prefix := "/dev/i2c-" + items, err := filepath.Glob(prefix + "*") + if err != nil { + return true, err + } + if len(items) == 0 { + return false, errors.New("no I²C bus found") + } + // Make sure they are registered in order. + sort.Strings(items) + for _, item := range items { + bus, err := strconv.Atoi(item[len(prefix):]) + if err != nil { + continue + } + name := fmt.Sprintf("/dev/i2c-%d", bus) + d.buses = append(d.buses, name) + aliases := []string{fmt.Sprintf("I2C%d", bus)} + if err := i2creg.Register(name, aliases, bus, openerI2C(bus).Open); err != nil { + return true, err + } + } + return true, nil +} + +type openerI2C int + +func (o openerI2C) Open() (i2c.BusCloser, error) { + b, err := NewI2C(int(o)) + if err != nil { + return nil, err + } + return b, nil +} + +func init() { + if isLinux { + periph.MustRegister(&drvI2C) + } +} + +var drvI2C driverI2C + +var _ i2c.Bus = &I2C{} +var _ i2c.BusCloser = &I2C{} diff --git a/vendor/periph.io/x/periph/host/sysfs/led.go b/vendor/periph.io/x/periph/host/sysfs/led.go new file mode 100644 index 0000000..a60b923 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/led.go @@ -0,0 +1,267 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package sysfs + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "sort" + "strconv" + "sync" + "time" + + "periph.io/x/periph" + "periph.io/x/periph/conn" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/pin" + "periph.io/x/periph/host/fs" +) + +// LEDs is all the leds discovered on this host via sysfs. +// +// Depending on the user context, the LEDs may be read-only or writeable. +var LEDs []*LED + +// LEDByName returns a *LED for the LED name, if any. +// +// For all practical purpose, a LED is considered an output-only gpio.PinOut. +func LEDByName(name string) (*LED, error) { + // TODO(maruel): Use a bisect or a map. For now we don't expect more than a + // handful of LEDs so it doesn't matter. + for _, led := range LEDs { + if led.name == name { + if err := led.open(); err != nil { + return nil, err + } + return led, nil + } + } + return nil, errors.New("sysfs-led: invalid LED name") +} + +// LED represents one LED on the system. +type LED struct { + number int + name string + root string + + mu sync.Mutex + fBrightness *fs.File // handle to /sys/class/gpio/gpio*/direction; never closed +} + +// String implements conn.Resource. +func (l *LED) String() string { + return fmt.Sprintf("%s(%d)", l.name, l.number) +} + +// Halt implements conn.Resource. +// +// It turns the light off. +func (l *LED) Halt() error { + return l.Out(gpio.Low) +} + +// Name implements pin.Pin. +func (l *LED) Name() string { + return l.name +} + +// Number implements pin.Pin. +func (l *LED) Number() int { + return l.number +} + +// Function implements pin.Pin. +func (l *LED) Function() string { + if l.Read() { + return "LED/On" + } + return "LED/Off" +} + +// Func implements pin.PinFunc. +func (l *LED) Func() pin.Func { + if l.Read() { + return gpio.OUT_HIGH + } + return gpio.OUT_LOW +} + +// SupportedFuncs implements pin.PinFunc. +func (l *LED) SupportedFuncs() []pin.Func { + return []pin.Func{gpio.OUT} +} + +// SetFunc implements pin.PinFunc. +func (l *LED) SetFunc(f pin.Func) error { + switch f { + case gpio.OUT_HIGH: + return l.Out(gpio.High) + case gpio.OUT, gpio.OUT_LOW: + return l.Out(gpio.Low) + default: + return errors.New("sysfs-led: unsupported function") + } +} + +// In implements gpio.PinIn. +func (l *LED) In(pull gpio.Pull, edge gpio.Edge) error { + if pull != gpio.Float && pull != gpio.PullNoChange { + return errors.New("sysfs-led: pull is not supported on LED") + } + if edge != gpio.NoEdge { + return errors.New("sysfs-led: edge is not supported on LED") + } + return nil +} + +// Read implements gpio.PinIn. +func (l *LED) Read() gpio.Level { + err := l.open() + if err != nil { + return gpio.Low + } + l.mu.Lock() + defer l.mu.Unlock() + if _, err := l.fBrightness.Seek(0, 0); err != nil { + return gpio.Low + } + var b [4]byte + if _, err := l.fBrightness.Read(b[:]); err != nil { + return gpio.Low + } + if b[0] != '0' { + return gpio.High + } + return gpio.Low +} + +// WaitForEdge implements gpio.PinIn. +func (l *LED) WaitForEdge(timeout time.Duration) bool { + return false +} + +// Pull implements gpio.PinIn. +func (l *LED) Pull() gpio.Pull { + return gpio.PullNoChange +} + +// DefaultPull implements gpio.PinIn. +func (l *LED) DefaultPull() gpio.Pull { + return gpio.PullNoChange +} + +// Out implements gpio.PinOut. +func (l *LED) Out(level gpio.Level) error { + err := l.open() + if err != nil { + return err + } + l.mu.Lock() + defer l.mu.Unlock() + if _, err = l.fBrightness.Seek(0, 0); err != nil { + return err + } + if level { + _, err = l.fBrightness.Write([]byte("255")) + } else { + _, err = l.fBrightness.Write([]byte("0")) + } + return err +} + +// PWM implements gpio.PinOut. +// +// This sets the intensity level, if supported. The frequency is ignored. +func (l *LED) PWM(d gpio.Duty, f physic.Frequency) error { + err := l.open() + if err != nil { + return err + } + l.mu.Lock() + defer l.mu.Unlock() + if _, err = l.fBrightness.Seek(0, 0); err != nil { + return err + } + v := (d + gpio.DutyMax/512) / (gpio.DutyMax / 256) + _, err = l.fBrightness.Write([]byte(strconv.Itoa(int(v)))) + return err +} + +// + +func (l *LED) open() error { + l.mu.Lock() + defer l.mu.Unlock() + // trigger, max_brightness. + var err error + if l.fBrightness == nil { + p := l.root + "brightness" + if l.fBrightness, err = fs.Open(p, os.O_RDWR); err != nil { + // Retry with read-only. This is the default setting. + l.fBrightness, err = fs.Open(p, os.O_RDONLY) + } + } + return err +} + +// driverLED implements periph.Driver. +type driverLED struct { +} + +func (d *driverLED) String() string { + return "sysfs-led" +} + +func (d *driverLED) Prerequisites() []string { + return nil +} + +func (d *driverLED) After() []string { + return nil +} + +// Init initializes LEDs sysfs handling code. +// +// Uses led sysfs as described* at +// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-led +// +// * for the most minimalistic meaning of 'described'. +func (d *driverLED) Init() (bool, error) { + items, err := filepath.Glob("/sys/class/leds/*") + if err != nil { + return true, err + } + if len(items) == 0 { + return false, errors.New("no LED found") + } + // This make the LEDs in deterministic order. + sort.Strings(items) + for i, item := range items { + LEDs = append(LEDs, &LED{ + number: i, + name: filepath.Base(item), + root: item + "/", + }) + } + return true, nil +} + +func init() { + if isLinux { + periph.MustRegister(&drvLED) + } +} + +var drvLED driverLED + +var _ conn.Resource = &LED{} +var _ gpio.PinIn = &LED{} +var _ gpio.PinOut = &LED{} +var _ gpio.PinIO = &LED{} +var _ pin.PinFunc = &LED{} diff --git a/vendor/periph.io/x/periph/host/sysfs/spi.go b/vendor/periph.io/x/periph/host/sysfs/spi.go new file mode 100644 index 0000000..096220d --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/spi.go @@ -0,0 +1,651 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package sysfs + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "sync" + "unsafe" + + "periph.io/x/periph" + "periph.io/x/periph/conn" + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/conn/physic" + "periph.io/x/periph/conn/spi" + "periph.io/x/periph/conn/spi/spireg" + "periph.io/x/periph/host/fs" +) + +// NewSPI opens a SPI port via its devfs interface as described at +// https://www.kernel.org/doc/Documentation/spi/spidev and +// https://www.kernel.org/doc/Documentation/spi/spi-summary +// +// The resulting object is safe for concurrent use. +// +// busNumber is the bus number as exported by devfs. For example if the path is +// /dev/spidev0.1, busNumber should be 0 and chipSelect should be 1. +// +// It is recommended to use https://periph.io/x/periph/conn/spi/spireg#Open +// instead of using NewSPI() directly as the package sysfs is providing a +// Linux-specific implementation. periph.io works on many OSes! This permits +// it to work on all operating systems, or devices like SPI over USB. +func NewSPI(busNumber, chipSelect int) (*SPI, error) { + if isLinux { + return newSPI(busNumber, chipSelect) + } + return nil, errors.New("sysfs-spi: not implemented on non-linux OSes") +} + +// SPI is an open SPI port. +type SPI struct { + conn spiConn +} + +// Close closes the handle to the SPI driver. It is not a requirement to close +// before process termination. +// +// Note that the object is not reusable afterward. +func (s *SPI) Close() error { + s.conn.mu.Lock() + defer s.conn.mu.Unlock() + if err := s.conn.f.Close(); err != nil { + return fmt.Errorf("sysfs-spi: %v", err) + } + s.conn.f = nil + return nil +} + +func (s *SPI) String() string { + return s.conn.String() +} + +// LimitSpeed implements spi.ConnCloser. +func (s *SPI) LimitSpeed(f physic.Frequency) error { + if f > physic.GigaHertz { + return fmt.Errorf("sysfs-spi: invalid speed %s; maximum supported clock is 1GHz", f) + } + if f < 100*physic.Hertz { + return fmt.Errorf("sysfs-spi: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f) + } + s.conn.mu.Lock() + defer s.conn.mu.Unlock() + s.conn.freqPort = f + return nil +} + +// Connect implements spi.Port. +// +// It must be called before any I/O. +func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, error) { + if f > physic.GigaHertz { + return nil, fmt.Errorf("sysfs-spi: invalid speed %s; maximum supported clock is 1GHz", f) + } + if f < 100*physic.Hertz { + return nil, fmt.Errorf("sysfs-spi: invalid speed %s; minimum supported clock is 100Hz; did you forget to multiply by physic.MegaHertz?", f) + } + if mode&^(spi.Mode3|spi.HalfDuplex|spi.NoCS|spi.LSBFirst) != 0 { + return nil, fmt.Errorf("sysfs-spi: invalid mode %v", mode) + } + if bits < 1 || bits >= 256 { + return nil, fmt.Errorf("sysfs-spi: invalid bits %d", bits) + } + s.conn.mu.Lock() + defer s.conn.mu.Unlock() + if s.conn.connected { + return nil, errors.New("sysfs-spi: Connect() can only be called exactly once") + } + s.conn.connected = true + s.conn.freqConn = f + s.conn.bitsPerWord = uint8(bits) + // Only mode needs to be set via an IOCTL, others can be specified in the + // spiIOCTransfer packet, which saves a kernel call. + m := mode & spi.Mode3 + s.conn.muPins.Lock() + { + if mode&spi.HalfDuplex != 0 { + m |= threeWire + s.conn.halfDuplex = true + // In case initPins() had been called before Connect(). + s.conn.mosi = gpio.INVALID + } + if mode&spi.NoCS != 0 { + m |= noCS + s.conn.noCS = true + // In case initPins() had been called before Connect(). + s.conn.cs = gpio.INVALID + } + } + s.conn.muPins.Unlock() + if mode&spi.LSBFirst != 0 { + m |= lSBFirst + } + // Only the first 8 bits are used. This only works because the system is + // running in little endian. + if err := s.conn.setFlag(spiIOCMode, uint64(m)); err != nil { + return nil, fmt.Errorf("sysfs-spi: setting mode %v failed: %v", mode, err) + } + return &s.conn, nil +} + +// MaxTxSize implements conn.Limits +func (s *SPI) MaxTxSize() int { + return drvSPI.bufSize +} + +// CLK implements spi.Pins. +func (s *SPI) CLK() gpio.PinOut { + return s.conn.CLK() +} + +// MISO implements spi.Pins. +func (s *SPI) MISO() gpio.PinIn { + return s.conn.MISO() +} + +// MOSI implements spi.Pins. +func (s *SPI) MOSI() gpio.PinOut { + return s.conn.MOSI() +} + +// CS implements spi.Pins. +func (s *SPI) CS() gpio.PinOut { + return s.conn.CS() +} + +// Private details. + +func newSPI(busNumber, chipSelect int) (*SPI, error) { + if busNumber < 0 || busNumber >= 1<<16 { + return nil, fmt.Errorf("sysfs-spi: invalid bus %d", busNumber) + } + if chipSelect < 0 || chipSelect > 255 { + return nil, fmt.Errorf("sysfs-spi: invalid chip select %d", chipSelect) + } + // Use the devfs path for now. + f, err := ioctlOpen(fmt.Sprintf("/dev/spidev%d.%d", busNumber, chipSelect), os.O_RDWR) + if err != nil { + return nil, fmt.Errorf("sysfs-spi: %v", err) + } + return &SPI{ + spiConn{ + name: fmt.Sprintf("SPI%d.%d", busNumber, chipSelect), + f: f, + busNumber: busNumber, + chipSelect: chipSelect, + }, + }, nil +} + +// + +// spiConn implements spi.Conn. +type spiConn struct { + // Immutable + name string + f ioctlCloser + busNumber int + chipSelect int + + mu sync.Mutex + freqPort physic.Frequency // Frequency specified at LimitSpeed() + freqConn physic.Frequency // Frequency specified at Connect() + bitsPerWord uint8 + connected bool + halfDuplex bool + noCS bool + // Heap optimization: reduce the amount of memory allocations during + // transactions. + io [4]spiIOCTransfer + p [2]spi.Packet + + // Use a separate lock for the pins, so that they can be queried while a + // transaction is happening. + muPins sync.Mutex + clk gpio.PinOut + mosi gpio.PinOut + miso gpio.PinIn + cs gpio.PinOut +} + +func (s *spiConn) String() string { + return s.name +} + +// Read implements io.Reader. +func (s *spiConn) Read(b []byte) (int, error) { + if len(b) == 0 { + return 0, errors.New("sysfs-spi: Read() with empty buffer") + } + if drvSPI.bufSize != 0 && len(b) > drvSPI.bufSize { + return 0, fmt.Errorf("sysfs-spi: maximum Read length is %d, got %d bytes", drvSPI.bufSize, len(b)) + } + s.mu.Lock() + defer s.mu.Unlock() + s.p[0].W = nil + s.p[0].R = b + if err := s.txPackets(s.p[:1]); err != nil { + return 0, fmt.Errorf("sysfs-spi: Read() failed: %v", err) + } + return len(b), nil +} + +// Write implements io.Writer. +func (s *spiConn) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, errors.New("sysfs-spi: Write() with empty buffer") + } + if drvSPI.bufSize != 0 && len(b) > drvSPI.bufSize { + return 0, fmt.Errorf("sysfs-spi: maximum Write length is %d, got %d bytes", drvSPI.bufSize, len(b)) + } + s.mu.Lock() + defer s.mu.Unlock() + s.p[0].W = b + s.p[0].R = nil + if err := s.txPackets(s.p[:1]); err != nil { + return 0, fmt.Errorf("sysfs-spi: Write() failed: %v", err) + } + return len(b), nil +} + +// Tx sends and receives data simultaneously. +// +// It is OK if both w and r point to the same underlying byte slice. +// +// spidev enforces the maximum limit of transaction size. It can be as low as +// 4096 bytes. See the platform documentation to learn how to increase the +// limit. +func (s *spiConn) Tx(w, r []byte) error { + l := len(w) + if l == 0 { + if l = len(r); l == 0 { + return errors.New("sysfs-spi: Tx() with empty buffers") + } + } else { + // It's not a big deal to read halfDuplex without the lock. + if !s.halfDuplex && len(r) != 0 && len(r) != len(w) { + return fmt.Errorf("sysfs-spi: Tx(): when both w and r are used, they must be the same size; got %d and %d bytes", len(w), len(r)) + } + } + if drvSPI.bufSize != 0 && l > drvSPI.bufSize { + return fmt.Errorf("sysfs-spi: maximum Tx length is %d, got %d bytes", drvSPI.bufSize, l) + } + s.mu.Lock() + defer s.mu.Unlock() + s.p[0].W = w + s.p[0].R = r + p := s.p[:1] + if s.halfDuplex && len(w) != 0 && len(r) != 0 { + // Create two packets for HalfDuplex operation: one write then one read. + s.p[0].R = nil + s.p[0].KeepCS = true + s.p[1].W = nil + s.p[1].R = r + s.p[1].KeepCS = false + p = s.p[:2] + } else { + s.p[0].KeepCS = false + } + if err := s.txPackets(p); err != nil { + return fmt.Errorf("sysfs-spi: Tx() failed: %v", err) + } + return nil +} + +// TxPackets sends and receives packets as specified by the user. +// +// spidev enforces the maximum limit of transaction size. It can be as low as +// 4096 bytes. See the platform documentation to learn how to increase the +// limit. +func (s *spiConn) TxPackets(p []spi.Packet) error { + total := 0 + for i := range p { + lW := len(p[i].W) + lR := len(p[i].R) + if lW != lR && lW != 0 && lR != 0 { + return fmt.Errorf("sysfs-spi: when both w and r are used, they must be the same size; got %d and %d bytes", lW, lR) + } + l := lW + if l == 0 { + l = lR + } + total += l + } + if total == 0 { + return errors.New("sysfs-spi: empty packets") + } + if drvSPI.bufSize != 0 && total > drvSPI.bufSize { + return fmt.Errorf("sysfs-spi: maximum TxPackets length is %d, got %d bytes", drvSPI.bufSize, total) + } + + s.mu.Lock() + defer s.mu.Unlock() + if s.halfDuplex { + for i := range p { + if len(p[i].W) != 0 && len(p[i].R) != 0 { + return errors.New("sysfs-spi: can only specify one of w or r when in half duplex") + } + } + } + if err := s.txPackets(p); err != nil { + return fmt.Errorf("sysfs-spi: TxPackets() failed: %v", err) + } + return nil +} + +// Duplex implements conn.Conn. +func (s *spiConn) Duplex() conn.Duplex { + if s.halfDuplex { + return conn.Half + } + return conn.Full +} + +// MaxTxSize implements conn.Limits. +func (s *spiConn) MaxTxSize() int { + return drvSPI.bufSize +} + +// CLK implements spi.Pins. +func (s *spiConn) CLK() gpio.PinOut { + s.initPins() + return s.clk +} + +// MISO implements spi.Pins. +func (s *spiConn) MISO() gpio.PinIn { + s.initPins() + return s.miso +} + +// MOSI implements spi.Pins. +func (s *spiConn) MOSI() gpio.PinOut { + s.initPins() + return s.mosi +} + +// CS implements spi.Pins. +func (s *spiConn) CS() gpio.PinOut { + s.initPins() + return s.cs +} + +// + +func (s *spiConn) txPackets(p []spi.Packet) error { + // Convert the packets. + f := s.freqPort + if s.freqConn != 0 && (s.freqPort == 0 || s.freqConn < s.freqPort) { + f = s.freqConn + } + var m []spiIOCTransfer + if len(p) > len(s.io) { + m = make([]spiIOCTransfer, len(p)) + } else { + m = s.io[:len(p)] + } + for i := range p { + bits := p[i].BitsPerWord + if bits == 0 { + bits = s.bitsPerWord + } + csInvert := false + if !s.noCS { + // Invert CS behavior when a packet has KeepCS false, except for the last + // packet when KeepCS is true. + last := i == len(p)-1 + csInvert = p[i].KeepCS == last + } + m[i].reset(p[i].W, p[i].R, f, bits, csInvert) + } + return s.f.Ioctl(spiIOCTx(len(m)), uintptr(unsafe.Pointer(&m[0]))) +} + +func (s *spiConn) setFlag(op uint, arg uint64) error { + if err := s.f.Ioctl(op|0x40000000, uintptr(unsafe.Pointer(&arg))); err != nil { + return err + } + if false { + // Verification. + actual := uint64(0) + // getFlag() equivalent. + if err := s.f.Ioctl(op|0x80000000, uintptr(unsafe.Pointer(&actual))); err != nil { + return err + } + if actual != arg { + return fmt.Errorf("sysfs-spi: op 0x%x: set 0x%x, read 0x%x", op, arg, actual) + } + } + return nil +} + +func (s *spiConn) initPins() { + s.muPins.Lock() + defer s.muPins.Unlock() + if s.clk != nil { + return + } + if s.clk = gpioreg.ByName(fmt.Sprintf("SPI%d_CLK", s.busNumber)); s.clk == nil { + s.clk = gpio.INVALID + } + if s.miso = gpioreg.ByName(fmt.Sprintf("SPI%d_MISO", s.busNumber)); s.miso == nil { + s.miso = gpio.INVALID + } + // s.mosi is set to INVALID if HalfDuplex was specified. + if s.mosi != gpio.INVALID { + if s.mosi = gpioreg.ByName(fmt.Sprintf("SPI%d_MOSI", s.busNumber)); s.mosi == nil { + s.mosi = gpio.INVALID + } + } + // s.cs is set to INVALID if NoCS was specified. + if s.cs != gpio.INVALID { + if s.cs = gpioreg.ByName(fmt.Sprintf("SPI%d_CS%d", s.busNumber, s.chipSelect)); s.cs == nil { + s.cs = gpio.INVALID + } + } +} + +const ( + cSHigh spi.Mode = 0x4 // CS active high instead of default low (not recommended) + lSBFirst spi.Mode = 0x8 // Use little endian encoding for each word + threeWire spi.Mode = 0x10 // half-duplex; MOSI and MISO are shared + loop spi.Mode = 0x20 // loopback mode + noCS spi.Mode = 0x40 // do not assert CS + ready spi.Mode = 0x80 // slave pulls low to pause + // The driver optionally support dual and quad data lines. +) + +// spidev driver IOCTL control codes. +// +// Constants and structure definition can be found at +// /usr/include/linux/spi/spidev.h. +const ( + spiIOCMode = 0x16B01 // SPI_IOC_WR_MODE (8 bits) + spiIOLSBFirst = 0x16B02 // SPI_IOC_WR_LSB_FIRST + spiIOCBitsPerWord = 0x16B03 // SPI_IOC_WR_BITS_PER_WORD + spiIOCMaxSpeedHz = 0x46B04 // SPI_IOC_WR_MAX_SPEED_HZ + spiIOCMode32 = 0x46B05 // SPI_IOC_WR_MODE32 (32 bits) +) + +// spiIOCTx(l) calculates the equivalent of SPI_IOC_MESSAGE(l) to execute a +// transaction. +// +// The IOCTL for TX was deduced from this C code: +// +// #include "linux/spi/spidev.h" +// #include "sys/ioctl.h" +// #include +// int main() { +// for (int i = 1; i < 10; i++) { +// printf("len(%d) = 0x%08X\n", i, SPI_IOC_MESSAGE(i)); +// } +// return 0; +// } +// +// $ gcc a.cc && ./a.out +// len(1) = 0x40206B00 +// len(2) = 0x40406B00 +// len(3) = 0x40606B00 +// len(4) = 0x40806B00 +// len(5) = 0x40A06B00 +// len(6) = 0x40C06B00 +// len(7) = 0x40E06B00 +// len(8) = 0x41006B00 +// len(9) = 0x41206B00 +func spiIOCTx(l int) uint { + op := uint(0x40006B00) + return op | uint(0x200000)*uint(l) +} + +// spiIOCTransfer is spi_ioc_transfer in linux/spi/spidev.h. +// +// Also documented as struct spi_transfer at +// https://www.kernel.org/doc/html/latest/driver-api/spi.html +type spiIOCTransfer struct { + tx uint64 // Pointer to byte slice + rx uint64 // Pointer to byte slice + length uint32 // buffer length of tx and rx in bytes + speedHz uint32 // temporarily override the speed + delayUsecs uint16 // µs to sleep before selecting the device before the next transfer + bitsPerWord uint8 // temporarily override the number of bytes per word + csChange uint8 // true to deassert CS before next transfer + txNBits uint8 + rxNBits uint8 + pad uint16 +} + +func (s *spiIOCTransfer) reset(w, r []byte, f physic.Frequency, bitsPerWord uint8, csInvert bool) { + s.tx = 0 + s.rx = 0 + s.length = 0 + // w and r must be the same length. + if l := len(w); l != 0 { + s.tx = uint64(uintptr(unsafe.Pointer(&w[0]))) + s.length = uint32(l) + } + if l := len(r); l != 0 { + s.rx = uint64(uintptr(unsafe.Pointer(&r[0]))) + s.length = uint32(l) + } + s.speedHz = uint32((f + 500*physic.MilliHertz) / physic.Hertz) + s.delayUsecs = 0 + s.bitsPerWord = bitsPerWord + if csInvert { + s.csChange = 1 + } else { + s.csChange = 0 + } + s.txNBits = 0 + s.rxNBits = 0 + s.pad = 0 +} + +// + +// driverSPI implements periph.Driver. +type driverSPI struct { + // bufSize is the maximum number of bytes allowed per I/O on the SPI port. + bufSize int +} + +func (d *driverSPI) String() string { + return "sysfs-spi" +} + +func (d *driverSPI) Prerequisites() []string { + return nil +} + +func (d *driverSPI) After() []string { + return nil +} + +func (d *driverSPI) Init() (bool, error) { + // This driver is only registered on linux, so there is no legitimate time to + // skip it. + + // Do not use "/sys/bus/spi/devices/spi" as Raspbian's provided udev rules + // only modify the ACL of /dev/spidev* but not the ones in /sys/bus/... + prefix := "/dev/spidev" + items, err := filepath.Glob(prefix + "*") + if err != nil { + return true, err + } + if len(items) == 0 { + return false, errors.New("no SPI port found") + } + sort.Strings(items) + for _, item := range items { + parts := strings.Split(item[len(prefix):], ".") + if len(parts) != 2 { + continue + } + bus, err := strconv.Atoi(parts[0]) + if err != nil { + continue + } + cs, err := strconv.Atoi(parts[1]) + if err != nil { + continue + } + name := fmt.Sprintf("/dev/spidev%d.%d", bus, cs) + aliases := []string{fmt.Sprintf("SPI%d.%d", bus, cs)} + n := bus + if cs != 0 { + n = -1 + } + if err := spireg.Register(name, aliases, n, (&openerSPI{bus, cs}).Open); err != nil { + return true, err + } + } + f, err := fs.Open("/sys/module/spidev/parameters/bufsiz", os.O_RDONLY) + if err != nil { + return true, err + } + defer f.Close() + b, err := ioutil.ReadAll(f) + if err != nil { + return true, err + } + // Update the global value. + drvSPI.bufSize, err = strconv.Atoi(strings.TrimSpace(string(b))) + return true, err +} + +type openerSPI struct { + bus int + cs int +} + +func (o *openerSPI) Open() (spi.PortCloser, error) { + return NewSPI(o.bus, o.cs) +} + +func init() { + if isLinux { + periph.MustRegister(&drvSPI) + } +} + +var drvSPI driverSPI + +var _ conn.Limits = &SPI{} +var _ conn.Limits = &spiConn{} +var _ io.Reader = &spiConn{} +var _ io.Writer = &spiConn{} +var _ spi.Conn = &spiConn{} +var _ spi.Pins = &SPI{} +var _ spi.Pins = &spiConn{} +var _ spi.Port = &SPI{} +var _ spi.PortCloser = &SPI{} +var _ fmt.Stringer = &SPI{} diff --git a/vendor/periph.io/x/periph/host/sysfs/sysfs.go b/vendor/periph.io/x/periph/host/sysfs/sysfs.go new file mode 100644 index 0000000..3cfaf49 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/sysfs.go @@ -0,0 +1,62 @@ +// Copyright 2017 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package sysfs + +import ( + "io" + + "periph.io/x/periph/host/fs" +) + +var ioctlOpen = ioctlOpenDefault + +func ioctlOpenDefault(path string, flag int) (ioctlCloser, error) { + f, err := fs.Open(path, flag) + if err != nil { + return nil, err + } + return f, nil +} + +var fileIOOpen = fileIOOpenDefault + +func fileIOOpenDefault(path string, flag int) (fileIO, error) { + f, err := fs.Open(path, flag) + if err != nil { + return nil, err + } + return f, nil +} + +type ioctlCloser interface { + io.Closer + fs.Ioctler +} + +type fileIO interface { + Fd() uintptr + fs.Ioctler + io.Closer + io.Reader + io.Seeker + io.Writer +} + +// seekRead seeks to the beginning of a file and reads it. +func seekRead(f fileIO, b []byte) (int, error) { + if _, err := f.Seek(0, 0); err != nil { + return 0, err + } + return f.Read(b) +} + +// seekWrite seeks to the beginning of a file and writes to it. +func seekWrite(f fileIO, b []byte) error { + if _, err := f.Seek(0, 0); err != nil { + return err + } + _, err := f.Write(b) + return err +} diff --git a/vendor/periph.io/x/periph/host/sysfs/sysfs_linux.go b/vendor/periph.io/x/periph/host/sysfs/sysfs_linux.go new file mode 100644 index 0000000..735ca80 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/sysfs_linux.go @@ -0,0 +1,17 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package sysfs + +import ( + "os" + "syscall" +) + +const isLinux = true + +func isErrBusy(err error) bool { + e, ok := err.(*os.PathError) + return ok && e.Err == syscall.EBUSY +} diff --git a/vendor/periph.io/x/periph/host/sysfs/sysfs_other.go b/vendor/periph.io/x/periph/host/sysfs/sysfs_other.go new file mode 100644 index 0000000..3cdacd0 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/sysfs_other.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// +build !linux + +package sysfs + +const isLinux = false + +func isErrBusy(err error) bool { + // This function is not used on non-linux. + return false +} diff --git a/vendor/periph.io/x/periph/host/sysfs/thermal_sensor.go b/vendor/periph.io/x/periph/host/sysfs/thermal_sensor.go new file mode 100644 index 0000000..198d722 --- /dev/null +++ b/vendor/periph.io/x/periph/host/sysfs/thermal_sensor.go @@ -0,0 +1,199 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package sysfs + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "sort" + "strconv" + "sync" + "time" + + "periph.io/x/periph" + "periph.io/x/periph/conn" + "periph.io/x/periph/conn/physic" +) + +// ThermalSensors is all the sensors discovered on this host via sysfs. +var ThermalSensors []*ThermalSensor + +// ThermalSensorByName returns a *ThermalSensor for the sensor name, if any. +func ThermalSensorByName(name string) (*ThermalSensor, error) { + // TODO(maruel): Use a bisect or a map. For now we don't expect more than a + // handful of thermal sensors so it doesn't matter. + for _, t := range ThermalSensors { + if t.name == name { + if err := t.open(); err != nil { + return nil, err + } + return t, nil + } + } + return nil, errors.New("sysfs-thermal: invalid sensor name") +} + +// ThermalSensor represents one thermal sensor on the system. +type ThermalSensor struct { + name string + root string + + mu sync.Mutex + nameType string + f fileIO + precision physic.Temperature +} + +func (t *ThermalSensor) String() string { + return t.name +} + +// Halt implements conn.Resource. It is a noop. +func (t *ThermalSensor) Halt() error { + return nil +} + +// Type returns the type of sensor as exported by sysfs. +func (t *ThermalSensor) Type() string { + t.mu.Lock() + defer t.mu.Unlock() + if t.nameType == "" { + f, err := fileIOOpen(t.root+"type", os.O_RDONLY) + if err != nil { + return fmt.Sprintf("sysfs-thermal: %v", err) + } + defer f.Close() + var buf [256]byte + n, err := f.Read(buf[:]) + if err != nil { + return fmt.Sprintf("sysfs-thermal: %v", err) + } + if n < 2 { + t.nameType = "" + } else { + t.nameType = string(buf[:n-1]) + } + } + return t.nameType +} + +// Sense implements physic.SenseEnv. +func (t *ThermalSensor) Sense(e *physic.Env) error { + if err := t.open(); err != nil { + return err + } + t.mu.Lock() + defer t.mu.Unlock() + var buf [24]byte + n, err := seekRead(t.f, buf[:]) + if err != nil { + return fmt.Errorf("sysfs-thermal: %v", err) + } + if n < 2 { + return errors.New("sysfs-thermal: failed to read temperature") + } + i, err := strconv.Atoi(string(buf[:n-1])) + if err != nil { + return fmt.Errorf("sysfs-thermal: %v", err) + } + if t.precision == 0 { + t.precision = physic.MilliKelvin + if i < 100 { + t.precision *= 1000 + } + } + e.Temperature = physic.Temperature(i)*t.precision + physic.ZeroCelsius + return nil +} + +// SenseContinuous implements physic.SenseEnv. +func (t *ThermalSensor) SenseContinuous(interval time.Duration) (<-chan physic.Env, error) { + // TODO(maruel): Manually poll in a loop via time.NewTicker. + return nil, errors.New("sysfs-thermal: not implemented") +} + +// Precision implements physic.SenseEnv. +func (t *ThermalSensor) Precision(e *physic.Env) { + if t.precision == 0 { + dummy := physic.Env{} + // Ignore the error. + _ = t.Sense(&dummy) + } + t.mu.Lock() + defer t.mu.Unlock() + e.Temperature = t.precision +} + +// + +func (t *ThermalSensor) open() error { + t.mu.Lock() + defer t.mu.Unlock() + if t.f != nil { + return nil + } + f, err := fileIOOpen(t.root+"temp", os.O_RDONLY) + if err != nil { + return fmt.Errorf("sysfs-thermal: %v", err) + } + t.f = f + return nil +} + +// driverThermalSensor implements periph.Driver. +type driverThermalSensor struct { +} + +func (d *driverThermalSensor) String() string { + return "sysfs-thermal" +} + +func (d *driverThermalSensor) Prerequisites() []string { + return nil +} + +func (d *driverThermalSensor) After() []string { + return nil +} + +// Init initializes thermal sysfs handling code. +// +// Uses sysfs as described* at +// https://www.kernel.org/doc/Documentation/thermal/sysfs-api.txt +// +// * for the most minimalistic meaning of 'described'. +func (d *driverThermalSensor) Init() (bool, error) { + // This driver is only registered on linux, so there is no legitimate time to + // skip it. + items, err := filepath.Glob("/sys/class/thermal/*/temp") + if err != nil { + return true, err + } + if len(items) == 0 { + return false, errors.New("sysfs-thermal: no sensor found") + } + sort.Strings(items) + for _, item := range items { + base := filepath.Dir(item) + ThermalSensors = append(ThermalSensors, &ThermalSensor{ + name: filepath.Base(base), + root: base + "/", + }) + } + return true, nil +} + +func init() { + if isLinux { + periph.MustRegister(&drvThermalSensor) + } +} + +var drvThermalSensor driverThermalSensor + +var _ conn.Resource = &ThermalSensor{} +var _ physic.SenseEnv = &ThermalSensor{} diff --git a/vendor/periph.io/x/periph/host/videocore/videocore.go b/vendor/periph.io/x/periph/host/videocore/videocore.go new file mode 100644 index 0000000..1568a10 --- /dev/null +++ b/vendor/periph.io/x/periph/host/videocore/videocore.go @@ -0,0 +1,228 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package videocore interacts with the VideoCore GPU found on bcm283x. +// +// This package shouldn't be used directly, it is used by bcm283x's DMA +// implementation. +// +// Datasheet +// +// While not an actual datasheet, this is the closest to actual formal +// documentation: +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +package videocore + +import ( + "fmt" + "os" + "sync" + "unsafe" + + "periph.io/x/periph/host/fs" + "periph.io/x/periph/host/pmem" +) + +// Mem represents contiguous physically locked memory that was allocated by +// VideoCore. +// +// The memory is mapped in user space. +type Mem struct { + *pmem.View + handle uint32 +} + +// Close unmaps the physical memory allocation. +// +// It is important to call this function otherwise the memory is locked until +// the host reboots. +func (m *Mem) Close() error { + if err := m.View.Close(); err != nil { + return wrapf("failed to close physical view: %v", err) + } + if _, err := mailboxTx32(mbUnlockMemory, m.handle); err != nil { + return wrapf("failed to unlock memory: %v", err) + } + if _, err := mailboxTx32(mbReleaseMemory, m.handle); err != nil { + return wrapf("failed to release memory: %v", err) + } + return nil +} + +// Alloc allocates a continuous chunk of physical memory for use with DMA +// controller. +// +// Size must be rounded to 4Kb. +func Alloc(size int) (*Mem, error) { + if size <= 0 { + return nil, wrapf("memory size must be > 0; got %d", size) + } + if size&0xFFF != 0 { + return nil, wrapf("memory size must be rounded to 4096 pages; got %d", size) + } + if err := openMailbox(); err != nil { + return nil, wrapf("failed to open the mailbox to the GPU: %v", err) + } + // Size, Alignment, Flags; returns an opaque handle to be used to release the + // memory. + handle, err := mailboxTx32(mbAllocateMemory, uint32(size), 4096, flagDirect) + if err != nil { + return nil, wrapf("failed request to allocate memory: %v", err) + } + if handle == 0 { + return nil, wrapf("failed to allocate %d bytes", size) + } + // Lock the memory to retrieve a physical memory address. + p, err := mailboxTx32(mbLockMemory, handle) + if err != nil { + return nil, wrapf("failed request to lock memory: %v", err) + } + if p == 0 { + return nil, wrapf("failed to lock memory") + } + b, err := pmem.Map(uint64(p&^0xC0000000), size) + if err != nil { + return nil, wrapf("failed to memory map phyisical pages: %v", err) + } + return &Mem{View: b, handle: handle}, nil +} + +// + +var ( + mu sync.Mutex + mailbox messager + mailboxErr error +) + +const ( + mbIoctl = 0xc0046400 //_IOWR(0x100, 0, char *) + // All of these return anything but zero (‽) + mbFirmwareVersion = 0x1 // 0, 4 + mbBoardModel = 0x10001 // 0, 4 + mbBoardRevision = 0x10002 // 0, 4 + mbBoardMAC = 0x10003 // 0, 6 + mbBoardSerial = 0x10004 // 0, 8 + mbARMMemory = 0x10005 // 0, 8 + mbVCMemory = 0x10006 // 0, 8 + mbClocks = 0x10007 // 0, variable + // These work: + mbAllocateMemory = 0x3000C // 12, 4 + mbLockMemory = 0x3000D // 4, 4 + mbUnlockMemory = 0x3000E // 4, 4 + mbReleaseMemory = 0x3000F // 4, 4 + mbReply = 0x80000000 // High bit means a reply + + flagDiscardable = 1 << 0 // Can be resized to 0 at any time. Use for cached data. + flagNormal = 0 << 2 // Normal allocating alias. Don't use from ARM. + flagDirect = 1 << 2 // 0xCxxxxxxx Uncached + flagCoherent = 2 << 2 // 0x8xxxxxxx Non-allocating in L2 but coherent + flagL1Nonallocating = flagDirect | flagCoherent // Allocating in L2 + flagZero = 1 << 4 // Initialise buffer to all zeros + flagNoInit = 1 << 5 // Don't initialise (default is initialise to all ones + flagHintPermalock = 1 << 6 // Likely to be locked for long periods of time +) + +type messager interface { + sendMessage(b []uint32) error +} + +type messageBox struct { + f *fs.File +} + +func (m *messageBox) sendMessage(b []uint32) error { + return m.f.Ioctl(mbIoctl, uintptr(unsafe.Pointer(&b[0]))) +} + +func openMailbox() error { + mu.Lock() + defer mu.Unlock() + if mailbox != nil || mailboxErr != nil { + return mailboxErr + } + f, err := fs.Open("/dev/vcio", os.O_RDWR|os.O_SYNC) + mailboxErr = err + if err == nil { + mailbox = &messageBox{f} + mailboxErr = smokeTest() + } + return mailboxErr +} + +// genPacket creates a message to be sent to the GPU via the "mailbox". +// +// The message must be 16-byte aligned because only the upper 28 bits are +// passed; the lower bits are used to select the channel. +func genPacket(cmd uint32, replyLen uint32, args ...uint32) []uint32 { + p := make([]uint32, 48) + offset := uintptr(unsafe.Pointer(&p[0])) & 15 + b := p[16-offset : 32+16-offset] + max := uint32(len(args) * 4) + if replyLen > max { + max = replyLen + } + max = ((max + 3) / 4) * 4 + // size + zero + cmd + in + out + + zero + b[0] = uint32(6*4) + max // message total length in bytes, including trailing zero + b[2] = cmd // + b[3] = uint32(len(args)) * 4 // inputs length in bytes + b[4] = replyLen // outputs length in bytes + copy(b[5:], args) + return b[:6+max/4] +} + +func sendPacket(b []uint32) error { + if err := mailbox.sendMessage(b); err != nil { + return fmt.Errorf("failed to send IOCTL: %v", err) + } + if b[1] != mbReply { + // 0x80000001 means partial response. + return fmt.Errorf("got unexpected reply bit 0x%08x", b[1]) + } + return nil +} + +func mailboxTx32(cmd uint32, args ...uint32) (uint32, error) { + b := genPacket(cmd, 4, args...) + if err := sendPacket(b); err != nil { + return 0, err + } + if b[4] != mbReply|4 { + return 0, fmt.Errorf("got unexpected reply size 0x%08x", b[4]) + } + return b[5], nil +} + +/* +// mailboxTx is the generic version of mailboxTx32. It is not currently needed. +func mailboxTx(cmd uint32, reply []byte, args ...uint32) error { + b := genPacket(cmd, uint32(len(reply)), args...) + if err := sendPacket(b); err != nil { + return err + } + rep := b[4] + if rep&mbReply == 0 { + return fmt.Errorf("got unexpected reply size 0x%08x", b[4]) + } + rep &^= mbReply + if rep == 0 || rep > uint32(len(reply)) { + return fmt.Errorf("got unexpected reply size 0x%08x", b[4]) + } + return nil +} +*/ + +func smokeTest() error { + // It returns 0 on a RPi3 but don't assert this in case the VC firmware gets + // updated. + _, err := mailboxTx32(mbFirmwareVersion) + return err +} + +func wrapf(format string, a ...interface{}) error { + return fmt.Errorf("videocore: "+format, a...) +} + +var _ pmem.Mem = &Mem{} diff --git a/vendor/periph.io/x/periph/periph.go b/vendor/periph.io/x/periph/periph.go new file mode 100644 index 0000000..d4e3b2c --- /dev/null +++ b/vendor/periph.io/x/periph/periph.go @@ -0,0 +1,298 @@ +// Copyright 2016 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// Package periph is a peripheral I/O library. +// +// Package periph acts as a registry of drivers. It is focused on providing +// high quality host drivers that provide high-speed access to the hardware on +// the host computer itself. +// +// To learn more about the goals and design, visit https://periph.io/ +// +// Every device driver should register itself in its package init() function by +// calling periph.MustRegister(). +// +// User shall call either host.Init() or hostextra.Init() on startup to +// initialize all the registered drivers. +// +// Cmd +// +// cmd/ contains executable tools to communicate directly with the devices or +// the buses. +// +// cmd/ is allowed to import from conn/, devices/ and host/. +// +// Conn +// +// conn/ contains interfaces and registries for all the supported protocols and +// connections (I²C, SPI, GPIO, etc). +// +// conn/ is not allowed to import from any other package. +// +// Devices +// +// devices/ contains devices drivers that are connected to bus, port or +// connection (i.e I²C, SPI, 1-wire, GPIO) that can be controlled by the host, +// i.e. ssd1306 (display controller), bm280 (environmental sensor), etc. +// +// devices/ is allowed to import from conn/ and host/. +// +// Experimental +// +// experimental/ contains the drivers that are in the experimental area, not +// yet considered stable. See +// https://periph.io/project/#driver-lifetime-management for the process to +// move drivers out of this area. +// +// experimental/ is allowed to import from conn/, devices/ and host/. +// +// Host +// +// host/ contains all the implementations relating to the host itself, the CPU +// and buses that are exposed by the host onto which devices can be connected, +// i.e. I²C, SPI, GPIO, etc. +// +// host/ is allowed to import from conn/ only. +package periph // import "periph.io/x/periph" + +import ( + "errors" + "strconv" + "strings" + "sync" +) + +// Driver is an implementation for a protocol. +type Driver interface { + // String returns the name of the driver, as to be presented to the user. + // + // It must be unique in the list of registered drivers. + String() string + // Prerequisites returns a list of drivers that must be successfully loaded + // first before attempting to load this driver. + // + // A driver listing a prerequisite not registered is a fatal failure at + // initialization time. + Prerequisites() []string + // After returns a list of drivers that must be loaded first before + // attempting to load this driver. + // + // Unlike Prerequisites(), this driver will still be attempted even if the + // listed driver is missing or failed to load. + // + // This permits serialization without hard requirement. + After() []string + // Init initializes the driver. + // + // A driver may enter one of the three following state: loaded successfully, + // was skipped as irrelevant on this host, failed to load. + // + // On success, it must return true, nil. + // + // When irrelevant (skipped), it must return false, errors.New(). + // + // On failure, it must return true, errors.New(). The failure must + // state why it failed, for example an expected OS provided driver couldn't + // be opened, e.g. /dev/gpiomem on Raspbian. + Init() (bool, error) +} + +// DriverFailure is a driver that wasn't loaded, either because it was skipped +// or because it failed to load. +type DriverFailure struct { + D Driver + Err error +} + +func (d DriverFailure) String() string { + out := d.D.String() + ": " + if d.Err != nil { + out += d.Err.Error() + } else { + out += "" + } + return out +} + +// State is the state of loaded device drivers. +// +// Each list is sorted by the driver name. +type State struct { + Loaded []Driver + Skipped []DriverFailure + Failed []DriverFailure +} + +// Init initialises all the relevant drivers. +// +// Drivers are started concurrently. +// +// It is safe to call this function multiple times, the previous state is +// returned on later calls. +// +// Users will want to use host.Init(), which guarantees a baseline of included +// host drivers. +func Init() (*State, error) { + mu.Lock() + defer mu.Unlock() + if state != nil { + return state, nil + } + return initImpl() +} + +// Register registers a driver to be initialized automatically on Init(). +// +// The d.String() value must be unique across all registered drivers. +// +// It is an error to call Register() after Init() was called. +func Register(d Driver) error { + mu.Lock() + defer mu.Unlock() + if state != nil { + return errors.New("periph: can't call Register() after Init()") + } + + n := d.String() + if _, ok := byName[n]; ok { + return errors.New("periph: driver with same name " + strconv.Quote(n) + " was already registered") + } + byName[n] = d + return nil +} + +// MustRegister calls Register() and panics if registration fails. +// +// This is the function to call in a driver's package init() function. +func MustRegister(d Driver) { + if err := Register(d); err != nil { + panic(err) + } +} + +// + +var ( + // mu guards byName and state. + // - byName is only mutated by Register(). + // - state is only mutated by Init(). + // + // Once Init() is called, Register() refuses registering more drivers, thus + // byName is immutable once Init() started. + mu sync.Mutex + byName = map[string]Driver{} + state *State +) + +// stage is a set of drivers that can be loaded in parallel. +type stage struct { + // Subset of byName drivers, for the ones in this stage. + drvs map[string]Driver +} + +// explodeStages creates one or multiple stages by processing byName. +// +// It searches if there's any driver than has dependency on another driver and +// create stages from this DAG. +// +// It also verifies that there is not cycle in the DAG. +// +// When this function starts, allDriver and byName are guaranteed to be +// immutable. state must not be touched by this function. +func explodeStages() ([]*stage, error) { + // First, create the DAG. + dag := map[string]map[string]struct{}{} + for name, d := range byName { + m := map[string]struct{}{} + for _, p := range d.Prerequisites() { + if _, ok := byName[p]; !ok { + return nil, errors.New("periph: unsatisfied dependency " + strconv.Quote(name) + "->" + strconv.Quote(p) + "; it is missing; skipping") + } + m[p] = struct{}{} + } + for _, p := range d.After() { + // Skip undefined drivers silently, unlike Prerequisites(). + if _, ok := byName[p]; ok { + m[p] = struct{}{} + } + } + dag[name] = m + } + + // Create stages. + var stages []*stage + for len(dag) != 0 { + s := &stage{drvs: map[string]Driver{}} + for name, deps := range dag { + // This driver has no dependency, add it to the current stage. + if len(deps) == 0 { + s.drvs[name] = byName[name] + delete(dag, name) + } + } + if len(s.drvs) == 0 { + // Print out the remaining DAG so users can diagnose. + // It'd probably be nicer if it were done in Register()? + s := make([]string, 0, len(dag)) + for name, deps := range dag { + x := make([]string, 0, len(deps)) + for d := range deps { + x = insertString(x, d) + } + s = insertString(s, name+": "+strings.Join(x, ", ")) + } + return nil, errors.New("periph: found cycle(s) in drivers dependencies:\n" + strings.Join(s, "\n")) + } + stages = append(stages, s) + + // Trim the dependencies for the items remaining in the dag. + for passed := range s.drvs { + for name := range dag { + delete(dag[name], passed) + } + } + } + return stages, nil +} + +func insertDriver(l []Driver, d Driver) []Driver { + n := d.String() + i := search(len(l), func(i int) bool { return l[i].String() > n }) + l = append(l, nil) + copy(l[i+1:], l[i:]) + l[i] = d + return l +} + +func insertDriverFailure(l []DriverFailure, f DriverFailure) []DriverFailure { + n := f.String() + i := search(len(l), func(i int) bool { return l[i].String() > n }) + l = append(l, DriverFailure{}) + copy(l[i+1:], l[i:]) + l[i] = f + return l +} + +func insertString(l []string, s string) []string { + i := search(len(l), func(i int) bool { return l[i] > s }) + l = append(l, "") + copy(l[i+1:], l[i:]) + l[i] = s + return l +} + +// search implements the same algorithm as sort.Search(). +// +// It was extracted to to not depend on sort, which depends on reflect. +func search(n int, f func(int) bool) int { + lo := 0 + for hi := n; lo < hi; { + if i := int(uint(lo+hi) >> 1); !f(i) { + lo = i + 1 + } else { + hi = i + } + } + return lo +} diff --git a/vendor/periph.io/x/periph/periph_parallel.go b/vendor/periph.io/x/periph/periph_parallel.go new file mode 100644 index 0000000..656e3f0 --- /dev/null +++ b/vendor/periph.io/x/periph/periph_parallel.go @@ -0,0 +1,101 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// This file contains the parallelized driver loading logic. It is meant to be +// load the drivers as fast as possible by parallelising work. + +// +build !tinygo + +package periph + +import ( + "errors" + "strconv" + "sync" +) + +func initImpl() (*State, error) { + state = &State{} + // At this point, byName is guaranteed to be immutable. + cD := make(chan Driver) + cS := make(chan DriverFailure) + cE := make(chan DriverFailure) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for d := range cD { + state.Loaded = insertDriver(state.Loaded, d) + } + }() + wg.Add(1) + go func() { + defer wg.Done() + for f := range cS { + state.Skipped = insertDriverFailure(state.Skipped, f) + } + }() + wg.Add(1) + go func() { + defer wg.Done() + for f := range cE { + state.Failed = insertDriverFailure(state.Failed, f) + } + }() + + stages, err := explodeStages() + if err != nil { + return state, err + } + loaded := make(map[string]struct{}, len(byName)) + for _, s := range stages { + s.loadParallel(loaded, cD, cS, cE) + } + close(cD) + close(cS) + close(cE) + wg.Wait() + return state, nil +} + +// loadParallel loads all the drivers for this stage in parallel. +// +// Updates loaded in a safe way. +func (s *stage) loadParallel(loaded map[string]struct{}, cD chan<- Driver, cS, cE chan<- DriverFailure) { + success := make(chan string) + go func() { + defer close(success) + wg := sync.WaitGroup{} + loop: + for name, drv := range s.drvs { + // Intentionally do not look at After(), only Prerequisites(). + for _, dep := range drv.Prerequisites() { + if _, ok := loaded[dep]; !ok { + cS <- DriverFailure{drv, errors.New("dependency not loaded: " + strconv.Quote(dep))} + continue loop + } + } + + // Not skipped driver, attempt loading in a goroutine. + wg.Add(1) + go func(n string, d Driver) { + defer wg.Done() + if ok, err := d.Init(); ok { + if err == nil { + cD <- d + success <- n + return + } + cE <- DriverFailure{d, err} + } else { + cS <- DriverFailure{d, err} + } + }(name, drv) + } + wg.Wait() + }() + for s := range success { + loaded[s] = struct{}{} + } +} diff --git a/vendor/periph.io/x/periph/periph_serial.go b/vendor/periph.io/x/periph/periph_serial.go new file mode 100644 index 0000000..3fa70c2 --- /dev/null +++ b/vendor/periph.io/x/periph/periph_serial.go @@ -0,0 +1,55 @@ +// Copyright 2018 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +// This file contains the single threaded driver loading code, to be used on +// low performance cores. + +// +build tinygo + +package periph + +import ( + "errors" + "strconv" +) + +func initImpl() (*State, error) { + state = &State{} + // At this point, byName is guaranteed to be immutable. + stages, err := explodeStages() + if err != nil { + return state, err + } + loaded := make(map[string]struct{}, len(byName)) + for _, s := range stages { + s.loadSerial(state, loaded) + } + return state, nil +} + +// loadSerial loads all the drivers for this stage, one after the other. +func (s *stage) loadSerial(state *State, loaded map[string]struct{}) { + for name, drv := range s.drvs { + // Intentionally do not look at After(), only Prerequisites(). + for _, dep := range drv.Prerequisites() { + if _, ok := loaded[dep]; !ok { + state.Skipped = insertDriverFailure(state.Skipped, DriverFailure{drv, errors.New("dependency not loaded: " + strconv.Quote(dep))}) + goto loop + } + } + + // Not skipped driver, attempt loading in a goroutine. + if ok, err := drv.Init(); ok { + if err == nil { + state.Loaded = insertDriver(state.Loaded, drv) + loaded[name] = struct{}{} + } else { + state.Failed = insertDriverFailure(state.Failed, DriverFailure{drv, err}) + } + } else { + state.Skipped = insertDriverFailure(state.Skipped, DriverFailure{drv, err}) + } + loop: + } +}