├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ └── test.yml ├── .gohci.yml ├── AUTHORS ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── allwinner ├── a20.go ├── a64.go ├── allwinner_arm.go ├── allwinner_arm64.go ├── allwinner_other.go ├── allwinnersmoketest │ ├── allwinnersmoketest.go │ ├── benchmark.go │ ├── benchmark_fast_gpio_support.go │ ├── benchmark_gpio_support.go │ └── benchmark_gpio_support_test.go ├── clock.go ├── detect.go ├── dma.go ├── doc.go ├── gpio.go ├── gpio_pl.go ├── h3.go ├── h5.go ├── pwm.go ├── r8.go ├── spi.go └── timer.go ├── am335x ├── am335x.go ├── am335x_arm.go ├── am335x_other.go └── doc.go ├── bcm283x ├── bcm283x_arm.go ├── bcm283x_arm64.go ├── bcm283x_other.go ├── bcm283x_test.go ├── bcm283xsmoketest │ ├── bcm283xsmoketest.go │ ├── benchmark.go │ ├── benchmark_fast_gpio_support.go │ ├── benchmark_gpio_support.go │ └── benchmark_gpio_support_test.go ├── clock.go ├── clock_test.go ├── dma.go ├── dma_test.go ├── doc.go ├── example_test.go ├── gpio.go ├── gpio_test.go ├── pcm.go ├── pwm.go ├── pwm_test.go ├── streams.go ├── streams_test.go ├── timer.go └── timer_test.go ├── beagle ├── beagle.go ├── beagle_arm.go ├── beagle_other.go ├── black │ ├── black.go │ ├── black_arm.go │ └── black_other.go ├── bone │ ├── bone.go │ ├── bone_arm.go │ └── bone_other.go ├── doc.go └── green │ ├── green.go │ ├── green_arm.go │ └── green_other.go ├── chip ├── chip.go ├── chip_arm.go ├── chip_other.go ├── chipsmoketest │ └── chipsmoketest.go └── doc.go ├── codecov.yml ├── cpu ├── cpu.go ├── cpu_linux.go ├── cpu_other.go ├── cpu_test.go └── doc.go ├── distro ├── devtree.go ├── devtree_test.go ├── distro.go ├── distro_arm.go ├── distro_arm64.go ├── distro_linux.go ├── distro_nonarm.go ├── distro_nonlinux.go └── distro_test.go ├── doc.go ├── example_test.go ├── fs ├── fs.go ├── fs_linux.go ├── fs_other.go ├── ioctl.go ├── ioctl_mips_like.go └── ioctl_other.go ├── ftdi ├── debian │ └── 98-ft232h.rules ├── debug.go ├── dev.go ├── doc.go ├── driver.go ├── driver_test.go ├── eeprom.go ├── example_test.go ├── ftdi.go ├── ftdismoketest │ ├── README.md │ └── ftdismoketest.go ├── gpio.go ├── handle.go ├── i2c.go ├── mpsse.go ├── mpsse_gpio.go ├── no_debug.go ├── pin.go └── spi.go ├── go.mod ├── go.sum ├── gpioioctl ├── README.md ├── basic_test.go ├── doc.go ├── dummy.go ├── example_test.go ├── gpio.go ├── gpio_other.go ├── ioctl.go ├── lineset.go ├── syscall.go └── syscall_other.go ├── host.go ├── host_arm.go ├── host_arm64.go ├── host_linux.go ├── host_test.go ├── mt7688 ├── driver.go ├── gpio.go ├── mt7688.go ├── mt7688_mips_like.go ├── mt7688_other.go └── pin.go ├── nanopi ├── doc.go ├── nanopi.go ├── nanopi_arm.go ├── nanopi_arm64.go └── nanopi_other.go ├── netlink ├── doc.go ├── netlink_linux.go ├── netlink_other.go └── onewire.go ├── odroidc1 ├── doc.go ├── odroidc1.go ├── odroidc1_arm.go ├── odroidc1_other.go └── odroidc1smoketest │ └── odroidc1smoketest.go ├── orangepi ├── doc.go ├── orangepi.go ├── orangepi_arm.go ├── orangepi_arm64.go └── orangepi_other.go ├── pine64 ├── doc.go ├── pine64.go ├── pine64_arm.go ├── pine64_arm64.go └── pine64_other.go ├── pmem ├── alloc.go ├── alloc_test.go ├── doc.go ├── example_test.go ├── mem_linux.go ├── mem_other.go ├── pagemap.go ├── pagemap_test.go ├── smoketest.go ├── smoketest_test.go ├── view.go └── view_test.go ├── pru ├── doc.go ├── pru.go ├── pru_arm.go └── pru_other.go ├── rpi ├── doc.go ├── rpi.go ├── rpi_arm.go ├── rpi_arm64.go ├── rpi_other.go └── rpi_test.go ├── serial ├── serial.go ├── serial_fast.go ├── serial_not_win.go ├── serial_other.go ├── serial_slow.go └── serial_windows.go ├── sysfs ├── doc.go ├── example_test.go ├── fs_linux.go ├── fs_linux_test.go ├── fs_other.go ├── gpio.go ├── gpio_test.go ├── i2c.go ├── i2c_test.go ├── led.go ├── led_test.go ├── spi.go ├── spi_test.go ├── sysfs.go ├── sysfs_linux.go ├── sysfs_other.go ├── sysfs_test.go ├── sysfssmoketest │ ├── benchmark.go │ ├── benchmark_gpio_support.go │ ├── benchmark_gpio_support_test.go │ └── sysfssmoketest.go ├── thermal_sensor.go └── thermal_sensor_test.go └── videocore ├── example_test.go ├── videocore.go └── videocore_test.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Signal a problem with host drivers. 4 | --- 5 | 6 | **Describe the bug** 7 | A clear and concise description of what the bug is. 8 | 9 | **To Reproduce** 10 | Steps to reproduce the behavior: 11 | 1. Run program 12 | ```go 13 | package main 14 | 15 | import ( 16 | "periph.io/x/host/v3" 17 | ) 18 | 19 | func main() { 20 | host.Init() 21 | } 22 | ``` 23 | 2. Run it. 24 | 3. See error 25 | 26 | **Expected behavior** 27 | A clear and concise description of what you expected to happen. 28 | 29 | **Platform (please complete the following information):** 30 | - OS: [e.g. Raspbian Buster Lite] 31 | - Board [e.g. Raspberry Pi 4] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: What would you like to see supported? 4 | --- 5 | 6 | **What kind of new feature are you looking for?** 7 | (Keep the one that applies, please describe) 8 | - Hardware: [new chip] 9 | - Software: [new OS support, new integration, new logic package, etc] 10 | 11 | **Do you plan to:** 12 | - Contribute an initial driver: [Yes/No] 13 | - Write unit tests: [Yes/No] 14 | - Update https://github.com/periph/cmd to use the new functionality: [Yes/No] 15 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 1. Please prefix the issue title with the primary package affected. For example, 2 | if this PR fixes periph.io/x/host/v3/sysfs, prefix the PR title with "sysfs:". 3 | 4 | 2. Mention the issue number it fixes or add the details of the changes if it 5 | doesn't have a specific issue. Examples: 6 | - Fixes #12345 7 | - Helps with #12345 but doesn't not completely fix it. 8 | 9 | 3. Once integrated, send a PR to https://github.com/periph/cmd to leverage the 10 | new functionality (if relevant). 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the list of The Periph Authors for copyright purposes. 2 | # 3 | # This does not necessarily list everyone who has contributed code, since in 4 | # some cases, their employer may be the copyright holder. To see the full list 5 | # of contributors, see the revision history in source control. 6 | 7 | # Please keep the list sorted. 8 | 9 | Aaron Fischer 10 | Cássio Botaro 11 | Fractal Industries, Inc 12 | Google Inc. 13 | Josh Gardiner 14 | Marc-Antoine Ruel 15 | Matt Aimonetti 16 | Max Ekman 17 | Peter LoVerso 18 | Rifiniti, Inc 19 | Stephan Sperber 20 | Thorsten von Eicken 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for contributing to the project! Please look at [the periph contribution 4 | guidelines](https://periph.io/project/contributing/) first. 5 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the periph repository. 3 | # The AUTHORS file lists the copyright holders; this file 4 | # lists people. For example, Google employees are listed here 5 | # but not in AUTHORS, because Google holds the copyright. 6 | # 7 | # Names should be added to this file only after verifying that 8 | # the individual or the individual's organization has agreed to 9 | # the appropriate Contributor License Agreement, found here: 10 | # 11 | # https://cla.developers.google.com/ 12 | # 13 | # When adding J Random Contributor's name to this file, 14 | # either J's name or J's organization's name should be 15 | # added to the AUTHORS file, depending on whether the 16 | # individual or corporate CLA was used. 17 | 18 | # Names should be added to this file like so: 19 | # Individual's name 20 | # Individual's name 21 | # 22 | # An entry with multiple email addresses specifies that the 23 | # first address should be used in the submit logs and 24 | # that the other addresses should be recognized as the 25 | # same person when interacting with Gerrit. 26 | 27 | # Please keep the list sorted. 28 | 29 | Aaron Fischer 30 | Cássio Botaro 31 | Eugene Dzhurynsky 32 | Hidetoshi Shimokawa 33 | John Maguire 34 | Josh Gardiner 35 | Marc-Antoine Ruel 36 | Matt Aimonetti 37 | Max Ekman 38 | Matias Insaurralde 39 | Seán C McCord 40 | Stephan Sperber 41 | Thorsten von Eicken 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # periph - Peripherals I/O in Go 2 | 3 | Documentation is at https://periph.io 4 | 5 | Join us for a chat on 6 | [gophers.slack.com/messages/periph](https://gophers.slack.com/messages/periph), 7 | get an [invite here](https://invite.slack.golangbridge.org/). 8 | 9 | [![mascot](https://raw.githubusercontent.com/periph/website/master/site/static/img/periph-mascot-280.png)](https://periph.io/) 10 | 11 | [![PkgGoDev](https://pkg.go.dev/badge/periph.io/x/host/v3)](https://pkg.go.dev/periph.io/x/host/v3) 12 | [![codecov](https://codecov.io/gh/periph/host/branch/main/graph/badge.svg?token=RX9O1CPQHU)](https://codecov.io/gh/periph/host) 13 | 14 | 15 | ## Example 16 | 17 | Blink a LED: 18 | 19 | ~~~go 20 | package main 21 | 22 | import ( 23 | "time" 24 | "periph.io/x/conn/v3/gpio" 25 | "periph.io/x/host/v3" 26 | "periph.io/x/host/v3/rpi" 27 | ) 28 | 29 | func main() { 30 | host.Init() 31 | t := time.NewTicker(500 * time.Millisecond) 32 | for l := gpio.Low; ; l = !l { 33 | rpi.P1_33.Out(l) 34 | <-t.C 35 | } 36 | } 37 | ~~~ 38 | 39 | Curious? Look at [supported devices](https://periph.io/device/) for more 40 | examples! 41 | 42 | 43 | ## Authors 44 | 45 | `periph` was initiated with ❤️️ and passion by [Marc-Antoine 46 | Ruel](https://github.com/maruel). The full list of contributors is in 47 | [AUTHORS](https://github.com/periph/host/blob/main/AUTHORS) and 48 | [CONTRIBUTORS](https://github.com/periph/host/blob/main/CONTRIBUTORS). 49 | -------------------------------------------------------------------------------- /allwinner/allwinner_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package allwinner 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /allwinner/allwinner_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build arm64 6 | // +build arm64 7 | 8 | package allwinner 9 | 10 | const isArm = true 11 | -------------------------------------------------------------------------------- /allwinner/allwinner_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm && !arm64 6 | // +build !arm,!arm64 7 | 8 | package allwinner 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /allwinner/allwinnersmoketest/allwinnersmoketest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package allwinnersmoketest verifies that allwinner specific functionality 6 | // work. 7 | // 8 | // This test assumes GPIO pins are connected together. The exact ones depends 9 | // on the actual board. It is PB2 and PB3 for the C.H.I.P. 10 | package allwinnersmoketest 11 | 12 | import ( 13 | "errors" 14 | "flag" 15 | "fmt" 16 | "time" 17 | 18 | "periph.io/x/conn/v3/gpio" 19 | "periph.io/x/host/v3/allwinner" 20 | "periph.io/x/host/v3/chip" 21 | "periph.io/x/host/v3/pine64" 22 | ) 23 | 24 | // SmokeTest is imported by periph-smoketest. 25 | type SmokeTest struct { 26 | // start is to display the delta in µs. 27 | start time.Time 28 | } 29 | 30 | // Name implements the SmokeTest interface. 31 | func (s *SmokeTest) Name() string { 32 | return "allwinner" 33 | } 34 | 35 | // Description implements the SmokeTest interface. 36 | func (s *SmokeTest) Description() string { 37 | return "Tests advanced Allwinner functionality" 38 | } 39 | 40 | // Run implements the SmokeTest interface. 41 | func (s *SmokeTest) Run(f *flag.FlagSet, args []string) error { 42 | if err := f.Parse(args); err != nil { 43 | return err 44 | } 45 | if f.NArg() != 0 { 46 | f.Usage() 47 | return errors.New("unrecognized arguments") 48 | } 49 | if !allwinner.Present() { 50 | f.Usage() 51 | return errors.New("this smoke test can only be run on an allwinner based host") 52 | } 53 | 54 | start := time.Now() 55 | var pwm *loggingPin 56 | var other *loggingPin 57 | if chip.Present() { 58 | pwm = &loggingPin{allwinner.PB2, start} 59 | other = &loggingPin{allwinner.PB3, start} 60 | } else if pine64.Present() { 61 | //pwm = &loggingPin{allwinner.PD22} 62 | return errors.New("implement and test for pine64") 63 | } else { 64 | return errors.New("implement and test for this host") 65 | } 66 | return ensureConnectivity(pwm, other) 67 | } 68 | 69 | // Returns a channel that will return one bool, true if a edge was detected, 70 | // false otherwise. 71 | func (s *SmokeTest) waitForEdge(p gpio.PinIO) <-chan bool { 72 | c := make(chan bool) 73 | // A timeout inherently makes this test flaky but there's a inherent 74 | // assumption that the CPU edge trigger wakes up this process within a 75 | // reasonable amount of time; in term of latency. 76 | go func() { 77 | b := p.WaitForEdge(time.Second) 78 | // Author note: the test intentionally doesn't call p.Read() to test that 79 | // reading is not necessary. 80 | fmt.Printf(" %s -> WaitForEdge(%s) -> %t\n", since(s.start), p, b) 81 | c <- b 82 | }() 83 | return c 84 | } 85 | 86 | // 87 | 88 | func printPin(p gpio.PinIO) { 89 | fmt.Printf("- %s: %s", p, p.Function()) 90 | if r, ok := p.(gpio.RealPin); ok { 91 | fmt.Printf(" alias for %s", r.Real()) 92 | } 93 | fmt.Print("\n") 94 | } 95 | 96 | // since returns time in µs since the test start. 97 | func since(start time.Time) string { 98 | µs := (time.Since(start) + time.Microsecond/2) / time.Microsecond 99 | ms := µs / 1000 100 | µs %= 1000 101 | return fmt.Sprintf("%3d.%03dms", ms, µs) 102 | } 103 | 104 | // loggingPin logs when its state changes. 105 | type loggingPin struct { 106 | *allwinner.Pin 107 | start time.Time 108 | } 109 | 110 | func (p *loggingPin) In(pull gpio.Pull, edge gpio.Edge) error { 111 | fmt.Printf(" %s %s.In(%s, %s)\n", since(p.start), p, pull, edge) 112 | return p.Pin.In(pull, edge) 113 | } 114 | 115 | func (p *loggingPin) Out(l gpio.Level) error { 116 | fmt.Printf(" %s %s.Out(%s)\n", since(p.start), p, l) 117 | return p.Pin.Out(l) 118 | } 119 | 120 | // ensureConnectivity makes sure they are connected together. 121 | func ensureConnectivity(p1, p2 *loggingPin) error { 122 | if err := p1.In(gpio.PullDown, gpio.NoEdge); err != nil { 123 | return err 124 | } 125 | if err := p2.In(gpio.PullDown, gpio.NoEdge); err != nil { 126 | return err 127 | } 128 | time.Sleep(time.Microsecond) 129 | if p1.Read() != gpio.Low { 130 | return fmt.Errorf("unexpected %s value; expected low", p1) 131 | } 132 | if p2.Read() != gpio.Low { 133 | return fmt.Errorf("unexpected %s value; expected low", p2) 134 | } 135 | if err := p2.In(gpio.PullUp, gpio.NoEdge); err != nil { 136 | return err 137 | } 138 | time.Sleep(time.Microsecond) 139 | if p1.Read() != gpio.High { 140 | return fmt.Errorf("unexpected %s value; expected high", p1) 141 | } 142 | if err := p1.In(gpio.Float, gpio.NoEdge); err != nil { 143 | return err 144 | } 145 | return p2.In(gpio.Float, gpio.NoEdge) 146 | } 147 | -------------------------------------------------------------------------------- /allwinner/allwinnersmoketest/benchmark.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package allwinnersmoketest 6 | 7 | import ( 8 | "errors" 9 | "flag" 10 | "fmt" 11 | 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpioreg" 14 | "periph.io/x/host/v3/allwinner" 15 | ) 16 | 17 | // Benchmark is imported by periph-smoketest. 18 | type Benchmark struct { 19 | short bool 20 | p *allwinner.Pin 21 | pull gpio.Pull 22 | } 23 | 24 | // Name implements the SmokeTest interface. 25 | func (s *Benchmark) Name() string { 26 | return "allwinner-benchmark" 27 | } 28 | 29 | // Description implements the SmokeTest interface. 30 | func (s *Benchmark) Description() string { 31 | return "Benchmarks allwinner functionality" 32 | } 33 | 34 | // Run implements the SmokeTest interface. 35 | func (s *Benchmark) Run(f *flag.FlagSet, args []string) error { 36 | name := f.String("p", "", "Pin to use") 37 | f.BoolVar(&s.short, "short", false, "Skip many partially redundant benchmarks") 38 | if err := f.Parse(args); err != nil { 39 | return err 40 | } 41 | 42 | if f.NArg() != 0 { 43 | f.Usage() 44 | return errors.New("unsupported flags") 45 | } 46 | if !allwinner.Present() { 47 | f.Usage() 48 | return errors.New("this smoke test can only be run on an allwinner based host") 49 | } 50 | if *name == "" { 51 | f.Usage() 52 | return errors.New("-p is required") 53 | } 54 | p := gpioreg.ByName(*name) 55 | if p == nil { 56 | return fmt.Errorf("invalid pin %q", *name) 57 | } 58 | if r, ok := p.(gpio.RealPin); ok { 59 | p = r.Real() 60 | } 61 | var ok bool 62 | if s.p, ok = p.(*allwinner.Pin); !ok { 63 | return fmt.Errorf("pin is not allwinner %q", *name) 64 | } 65 | s.pull = gpio.PullDown 66 | s.runFastGPIOBenchmark() 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /allwinner/allwinnersmoketest/benchmark_gpio_support_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package allwinnersmoketest 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | func TestToHz(t *testing.T) { 15 | data := []struct { 16 | N int 17 | T time.Duration 18 | expected physic.Frequency 19 | }{ 20 | { 21 | 0, 22 | time.Second, 23 | 0, 24 | }, 25 | { 26 | 1, 27 | 0, 28 | 0, 29 | }, 30 | { 31 | 1, 32 | time.Millisecond, 33 | physic.KiloHertz, 34 | }, 35 | { 36 | 1, 37 | time.Second, 38 | physic.Hertz, 39 | }, 40 | { 41 | 3, 42 | 7 * time.Millisecond, 43 | // 3/7 with perfect rounding. 44 | 428571429 * physic.MicroHertz, 45 | }, 46 | { 47 | 3000, 48 | 7 * time.Microsecond, 49 | // 3/7 with perfect rounding. 50 | 428571428571429 * physic.MicroHertz, 51 | }, 52 | { 53 | 1000000, 54 | 1000 * time.Second, 55 | physic.KiloHertz, 56 | }, 57 | { 58 | 1000000, 59 | time.Second, 60 | physic.MegaHertz, 61 | }, 62 | { 63 | 1000000, 64 | time.Millisecond, 65 | physic.GigaHertz, 66 | }, 67 | { 68 | 1000000000, 69 | 1000 * time.Second, 70 | physic.MegaHertz, 71 | }, 72 | { 73 | 1000000000, 74 | time.Second, 75 | physic.GigaHertz, 76 | }, 77 | { 78 | 1234556000, 79 | // 2.3s. 80 | 2345567891 * time.Nanosecond, 81 | // 10 digits of resolution for 526.336MHz. 82 | 526335849711 * physic.MilliHertz, 83 | }, 84 | { 85 | 1000000000, 86 | time.Millisecond, 87 | physic.TeraHertz, 88 | }, 89 | { 90 | 300000000, 91 | 7 * time.Millisecond, 92 | // 3/7 with pretty good rounding, keeping in mind that's 42.857GHz. 93 | 42857142857143 * physic.MilliHertz, 94 | }, 95 | } 96 | for i, line := range data { 97 | r := testing.BenchmarkResult{N: line.N, T: line.T} 98 | if actual := toHz(&r); actual != line.expected { 99 | t.Fatalf("#%d: toHz(%d, %s) = %s(%d); expected %s(%d)", i, line.N, line.T, actual, actual, line.expected, line.expected) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /allwinner/detect.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package allwinner 6 | 7 | import ( 8 | "strings" 9 | "sync" 10 | 11 | "periph.io/x/host/v3/distro" 12 | ) 13 | 14 | // Present detects whether the host CPU is an Allwinner CPU. 15 | // 16 | // https://en.wikipedia.org/wiki/Allwinner_Technology 17 | func Present() bool { 18 | detection.do() 19 | return detection.isAllwinner 20 | } 21 | 22 | // IsR8 detects whether the host CPU is an Allwinner R8 CPU. 23 | // 24 | // It looks for the string "sun5i-r8" in /proc/device-tree/compatible. 25 | func IsR8() bool { 26 | detection.do() 27 | return detection.isR8 28 | } 29 | 30 | // IsA20 detects whether the host CPU is an Allwinner A20 CPU. 31 | // 32 | // It first looks for the string "sun71-a20" in /proc/device-tree/compatible, 33 | // and if that fails it checks for "Hardware : sun7i" in /proc/cpuinfo. 34 | func IsA20() bool { 35 | detection.do() 36 | return detection.isA20 37 | } 38 | 39 | // IsA64 detects whether the host CPU is an Allwinner A64 CPU. 40 | // 41 | // It looks for the string "sun50iw1p1" in /proc/device-tree/compatible. 42 | func IsA64() bool { 43 | detection.do() 44 | return detection.isA64 45 | } 46 | 47 | // IsH3 detects whether the host CPU is an Allwinner H3/H2+ Plus CPU. 48 | // 49 | // It looks for the string "sun8i-h2-plus" or "sun8i-h3" in /proc/device-tree/compatible. 50 | func IsH3() bool { 51 | detection.do() 52 | return detection.isH3 53 | } 54 | 55 | // IsH5 detects whether the host CPU is an Allwinner H5 CPU. 56 | // 57 | // It looks for the string "sun50i-h5" in /proc/device-tree/compatible. 58 | func IsH5() bool { 59 | detection.do() 60 | return detection.isH5 61 | } 62 | 63 | // 64 | 65 | type detectionS struct { 66 | mu sync.Mutex 67 | done bool 68 | isAllwinner bool 69 | isR8 bool 70 | isA20 bool 71 | isA64 bool 72 | isH3 bool 73 | isH5 bool 74 | } 75 | 76 | var detection detectionS 77 | 78 | // do contains the CPU detection logic that determines whether we have an 79 | // Allwinner CPU and if so, which exact model. 80 | // 81 | // Sadly there is no science behind this, it's more of a trial and error using 82 | // as many boards and OS flavors as possible. 83 | func (d *detectionS) do() { 84 | d.mu.Lock() 85 | defer d.mu.Unlock() 86 | if !d.done { 87 | d.done = true 88 | if isArm { 89 | for _, c := range distro.DTCompatible() { 90 | if strings.Contains(c, "sun50iw1p1") { 91 | d.isA64 = true 92 | } 93 | if strings.Contains(c, "sun5i-r8") { 94 | d.isR8 = true 95 | } 96 | if strings.Contains(c, "sun7i-a20") { 97 | d.isA20 = true 98 | } 99 | // H2+ is a subtype of H3 and nearly compatible (only lacks GBit MAC and 100 | // 4k HDMI Output), so it is safe to map H2+ as an H3. 101 | if strings.Contains(c, "sun8i-h2-plus") || strings.Contains(c, "sun8i-h3") { 102 | d.isH3 = true 103 | } 104 | if strings.Contains(c, "sun50i-h5") { 105 | d.isH5 = true 106 | } 107 | } 108 | d.isAllwinner = d.isA64 || d.isR8 || d.isA20 || d.isH3 || d.isH5 109 | 110 | if !d.isAllwinner { 111 | // The kernel in the image that comes pre-installed on the pcDuino3 Nano 112 | // is an old 3.x kernel that doesn't expose the device-tree in procfs, 113 | // so do an extra check in cpuinfo as well if we haven't detected 114 | // anything yet. 115 | // Distros based on 4.x kernels do expose it. 116 | if hw, ok := distro.CPUInfo()["Hardware"]; ok { 117 | if hw == "sun7i" { 118 | d.isA20 = true 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /allwinner/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package allwinner exposes the GPIO functionality that is common to all 6 | // AllWinner processors. 7 | // 8 | // This driver implements memory-mapped GPIO pin manipulation and leverages 9 | // sysfs-gpio for edge detection. 10 | // 11 | // If you are looking at the actual implementation, open doc.go for further 12 | // implementation details. 13 | // 14 | // # Datasheets 15 | // 16 | // A64: http://files.pine64.org/doc/datasheet/pine64/Allwinner_A64_User_Manual_V1.0.pdf 17 | // 18 | // H3: http://dl.linux-sunxi.org/H3/Allwinner_H3_Datasheet_V1.0.pdf 19 | // 20 | // R8: https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20User%20Manual%20V1.1.pdf 21 | // 22 | // Physical overview: http://files.pine64.org/doc/datasheet/pine64/A64_Datasheet_V1.1.pdf 23 | package allwinner 24 | 25 | // Other implementation details 26 | // 27 | // The most active kernel branch is 28 | // https://github.com/linux-sunxi/linux-sunxi/commits/sunxi-next 29 | // 30 | // In particular look at 31 | // https://github.com/linux-sunxi/linux-sunxi/blob/sunxi-next/drivers/dma/sun4i-dma.c 32 | // and 33 | // https://github.com/linux-sunxi/linux-sunxi/blob/sunxi-next/drivers/dma/sun6i-dma.c 34 | -------------------------------------------------------------------------------- /allwinner/pwm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package allwinner 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | const pwmClock = 24000000 14 | const pwmMaxPeriod = 0x10000 15 | 16 | // prescalers is the value for pwm0Prescale* 17 | var prescalers = []struct { 18 | freq uint32 19 | scaler pwmPrescale 20 | }{ 21 | // Base frequency (min freq is half that) / PWM clock at pwmMaxPeriod 22 | {pwmClock, pwmPrescale1}, // 24MHz / 366Hz 23 | {pwmClock / 120, pwmPrescale120}, // 200kHz / 3Hz 24 | {pwmClock / 180, pwmPrescale180}, // 133kHz / 2Hz 25 | {pwmClock / 240, pwmPrescale240}, // 100kHz / 1.5Hz 26 | {pwmClock / 360, pwmPrescale360}, // 66kHz / 1.01Hz 27 | {pwmClock / 480, pwmPrescale480}, // 50kHz / 0.7Hz 28 | {pwmClock / 12000, pwmPrescale12000}, // 2kHz 29 | {pwmClock / 24000, pwmPrescale24000}, // 1kHz 30 | {pwmClock / 36000, pwmPrescale36000}, // 666 Hz 31 | {pwmClock / 48000, pwmPrescale48000}, // 500 Hz 32 | {pwmClock / 72000, pwmPrescale72000}, // 333 Hz / 0.005Hz 33 | } 34 | 35 | const ( 36 | // 31:29 reserved 37 | pwmBusy pwmCtl = 1 << 28 // PWM0_RDY 38 | // 27:10 reserved (used for pwm1) 39 | pwm0Mask pwmCtl = (1 << 10) - 1 40 | pwm0Bypass pwmCtl = 1 << 9 // PWM0_BYPASS (marked as unused on some drivers?) 41 | pwm0PulseStart pwmCtl = 1 << 8 // PWM_CH0_PUL_START 42 | pwm0ModePulse pwmCtl = 1 << 7 // PWM_CHANNEL0_MODE 43 | pwm0SCLK pwmCtl = 1 << 6 // SCLK_CH0_GATING 44 | pwm0Polarity pwmCtl = 1 << 5 // PWM_CH0_ACT_STA 45 | pwm0Enable pwmCtl = 1 << 4 // PWM_CH0_EN 46 | // 3:0 47 | pwm0PrescaleMask pwmCtl = pwmCtl(pwmPrescaleMask) // PWM_CH0_PRESCAL 48 | pwm0Prescale120 pwmCtl = pwmCtl(pwmPrescale120) 49 | pwm0Prescale180 pwmCtl = pwmCtl(pwmPrescale180) 50 | pwm0Prescale240 pwmCtl = pwmCtl(pwmPrescale240) 51 | pwm0Prescale360 pwmCtl = pwmCtl(pwmPrescale360) 52 | pwm0Prescale480 pwmCtl = pwmCtl(pwmPrescale480) 53 | // 5, 6, 7 reserved 54 | pwm0Prescale12000 pwmCtl = pwmCtl(pwmPrescale12000) 55 | pwm0Prescale24000 pwmCtl = pwmCtl(pwmPrescale24000) 56 | pwm0Prescale36000 pwmCtl = pwmCtl(pwmPrescale36000) 57 | pwm0Prescale48000 pwmCtl = pwmCtl(pwmPrescale48000) 58 | pwm0Prescale72000 pwmCtl = pwmCtl(pwmPrescale72000) 59 | // 13, 14 reserved 60 | pwm0Prescale1 pwmCtl = pwmCtl(pwmPrescale1) 61 | ) 62 | 63 | // A64: Pages 194-195. 64 | // R8: Pages 83-84. 65 | type pwmCtl uint32 66 | 67 | func (p pwmCtl) String() string { 68 | var out []string 69 | if p&pwmBusy != 0 { 70 | out = append(out, "PWM0_RDY") 71 | p &^= pwmBusy 72 | } 73 | if p&pwm0Bypass != 0 { 74 | out = append(out, "PWM0_BYPASS") 75 | p &^= pwm0Bypass 76 | } 77 | if p&pwm0PulseStart != 0 { 78 | out = append(out, "PWM0_CH0_PUL_START") 79 | p &^= pwm0PulseStart 80 | } 81 | if p&pwm0ModePulse != 0 { 82 | out = append(out, "PWM0_CHANNEL0_MODE") 83 | p &^= pwm0ModePulse 84 | } 85 | if p&pwm0SCLK != 0 { 86 | out = append(out, "SCLK_CH0_GATING") 87 | p &^= pwm0SCLK 88 | } 89 | if p&pwm0Polarity != 0 { 90 | out = append(out, "PWM_CH0_ACT_STA") 91 | p &^= pwm0Polarity 92 | } 93 | if p&pwm0Enable != 0 { 94 | out = append(out, "PWM_CH0_EN") 95 | p &^= pwm0Enable 96 | } 97 | out = append(out, pwmPrescale(p&pwm0PrescaleMask).String()) 98 | p &^= pwm0PrescaleMask 99 | if p != 0 { 100 | out = append(out, fmt.Sprintf("Unknown(0x%08X)", uint32(p))) 101 | } 102 | return strings.Join(out, "|") 103 | } 104 | 105 | const ( 106 | pwmPrescaleMask pwmPrescale = 0xF 107 | pwmPrescale120 pwmPrescale = 0 108 | pwmPrescale180 pwmPrescale = 1 109 | pwmPrescale240 pwmPrescale = 2 110 | pwmPrescale360 pwmPrescale = 3 111 | pwmPrescale480 pwmPrescale = 4 112 | // 5, 6, 7 reserved 113 | pwmPrescale12000 pwmPrescale = 8 114 | pwmPrescale24000 pwmPrescale = 9 115 | pwmPrescale36000 pwmPrescale = 10 116 | pwmPrescale48000 pwmPrescale = 11 117 | pwmPrescale72000 pwmPrescale = 12 118 | // 13, 14 reserved 119 | pwmPrescale1 pwmPrescale = 15 120 | ) 121 | 122 | type pwmPrescale uint32 123 | 124 | func (p pwmPrescale) String() string { 125 | switch p { 126 | case pwmPrescale120: 127 | return "/120" 128 | case pwmPrescale180: 129 | return "/180" 130 | case pwmPrescale240: 131 | return "/240" 132 | case pwmPrescale360: 133 | return "/360" 134 | case pwmPrescale480: 135 | return "/480" 136 | case pwmPrescale12000: 137 | return "/12k" 138 | case pwmPrescale24000: 139 | return "/24k" 140 | case pwmPrescale36000: 141 | return "/36k" 142 | case pwmPrescale48000: 143 | return "/48k" 144 | case pwmPrescale72000: 145 | return "/72k" 146 | case pwmPrescale1: 147 | return "/1" 148 | default: 149 | return fmt.Sprintf("InvalidScalar(%d)", p&pwmPrescaleMask) 150 | } 151 | } 152 | 153 | // A64: Page 195. 154 | // R8: Page 84 155 | type pwmPeriod uint32 156 | 157 | func (p pwmPeriod) String() string { 158 | return fmt.Sprintf("%d/%d", p&0xFFFF, uint32((p>>16)&0xFFFF)+1) 159 | } 160 | 161 | func toPeriod(total uint32, active uint16) pwmPeriod { 162 | if total > pwmMaxPeriod { 163 | total = pwmMaxPeriod 164 | } 165 | return pwmPeriod(total-1)<<16 | pwmPeriod(active) 166 | } 167 | 168 | // getBestPrescale finds the best prescaler. 169 | // 170 | // Cycles must be between 2 and 0x10000/2. 171 | func getBestPrescale(period time.Duration) pwmPrescale { 172 | // TODO(maruel): Rewrite this function, it is incorrect. 173 | for _, v := range prescalers { 174 | p := time.Second / time.Duration(v.freq) 175 | smallest := (period / pwmMaxPeriod) 176 | largest := (period / 2) 177 | if p > smallest && p < largest { 178 | return v.scaler 179 | } 180 | } 181 | // Period is longer than 196s. 182 | return pwmPrescale72000 183 | } 184 | 185 | // pwmMap represents the PWM memory mapped CPU registers. 186 | // 187 | // The base frequency is 24Mhz. 188 | // 189 | // TODO(maruel): Some CPU have 2 PWMs. 190 | type pwmMap struct { 191 | ctl pwmCtl // PWM_CTRL_REG 192 | period pwmPeriod // PWM_CH0_PERIOD 193 | } 194 | 195 | func (p *pwmMap) String() string { 196 | return fmt.Sprintf("pwmMap{%s, %v}", p.ctl, p.period) 197 | } 198 | -------------------------------------------------------------------------------- /allwinner/r8.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // This file contains pin mapping information that is specific to the Allwinner 6 | // R8 model. 7 | 8 | package allwinner 9 | 10 | import ( 11 | "strings" 12 | 13 | "periph.io/x/conn/v3/pin" 14 | "periph.io/x/host/v3/sysfs" 15 | ) 16 | 17 | // R8 specific pins. 18 | var ( 19 | FEL *pin.BasicPin // Boot mode selection 20 | MIC_IN *pin.BasicPin // Microphone in 21 | MIC_GND *pin.BasicPin // Microphone ground 22 | HP_LEFT *pin.BasicPin // Left speaker out 23 | HP_RIGHT *pin.BasicPin // Right speaker out 24 | HP_COM *pin.BasicPin // Speaker common 25 | X1, X2, Y1, Y2 *pin.BasicPin // Touch screen pins 26 | ) 27 | 28 | // 29 | 30 | func init() { 31 | FEL = &pin.BasicPin{N: "FEL"} 32 | MIC_IN = &pin.BasicPin{N: "MIC_IN"} 33 | MIC_GND = &pin.BasicPin{N: "MIC_GND"} 34 | HP_LEFT = &pin.BasicPin{N: "HP_LEFT"} 35 | HP_RIGHT = &pin.BasicPin{N: "HP_RIGHT"} 36 | HP_COM = &pin.BasicPin{N: "HP_COM"} 37 | 38 | X1 = &pin.BasicPin{N: "X1"} 39 | X2 = &pin.BasicPin{N: "X2"} 40 | Y1 = &pin.BasicPin{N: "Y1"} 41 | Y2 = &pin.BasicPin{N: "Y2"} 42 | } 43 | 44 | // mappingR8 describes the mapping of each R8 processor gpio to their alternate 45 | // functions. 46 | // 47 | // It omits the in & out functions which are available on all pins. 48 | // 49 | // The mapping comes from the datasheet page 18: 50 | // https://github.com/NextThingCo/CHIP-Hardware/raw/master/CHIP%5Bv1_0%5D/CHIPv1_0-BOM-Datasheets/Allwinner%20R8%20Datasheet%20V1.2.pdf 51 | // 52 | // - The datasheet uses TWI instead of I2C but this is renamed here for consistency. 53 | var mappingR8 = map[string][5]pin.Func{ 54 | "PB0": {"I2C0_SCL"}, 55 | "PB1": {"I2C0_SDA"}, 56 | "PB2": {"PWM0", "", "", "", "EINT16"}, 57 | "PB3": {"IR_TX", "", "", "", "EINT17"}, 58 | "PB4": {"IR_RX", "", "", "", "EINT18"}, 59 | "PB10": {"SPI2_CS1"}, 60 | "PB15": {"I2C1_SCL"}, 61 | "PB16": {"I2C1_SDA"}, 62 | "PB17": {"I2C2_SCL"}, 63 | "PB18": {"I2C2_SDA"}, 64 | "PC0": {"NAND_WE", "SPI0_MOSI"}, 65 | "PC1": {"NAND_ALE", "SPI0_MISO"}, 66 | "PC2": {"NAND_CLE", "SPI0_CLK"}, 67 | "PC3": {"NAND_CE1", "SPI0_CS0"}, 68 | "PC4": {"NAND_CE0"}, 69 | "PC5": {"NAND_RE"}, 70 | "PC6": {"NAND_RB0", "SDC2_CMD"}, 71 | "PC7": {"NAND_RB1", "SDC2_CLK"}, 72 | "PC8": {"NAND_DQ0", "SDC2_D0"}, 73 | "PC9": {"NAND_DQ1", "SDC2_D1"}, 74 | "PC10": {"NAND_DQ2", "SDC2_D2"}, 75 | "PC11": {"NAND_DQ3", "SDC2_D3"}, 76 | "PC12": {"NAND_DQ4", "SDC2_D4"}, 77 | "PC13": {"NAND_DQ5", "SDC2_D5"}, 78 | "PC14": {"NAND_DQ6", "SDC2_D6"}, 79 | "PC15": {"NAND_DQ7", "SDC2_D7"}, 80 | "PC19": {""}, 81 | "PD2": {"LCD_D2", "UART2_TX"}, 82 | "PD3": {"LCD_D3", "UART2_RX"}, 83 | "PD4": {"LCD_D4", "UART2_CTX"}, 84 | "PD5": {"LCD_D5", "UART2_RTS"}, 85 | "PD6": {"LCD_D6", "ECRS"}, 86 | "PD7": {"LCD_D7", "ECOL"}, 87 | "PD10": {"LCD_D10", "ERXD0"}, 88 | "PD11": {"LCD_D11", "ERXD1"}, 89 | "PD12": {"LCD_D12", "ERXD2"}, 90 | "PD13": {"LCD_D13", "ERXD3"}, 91 | "PD14": {"LCD_D14", "ERXCK"}, 92 | "PD15": {"LCD_D15", "ERXERR"}, 93 | "PD18": {"LCD_D18", "ERXDV"}, 94 | "PD19": {"LCD_D19", "ETXD0"}, 95 | "PD20": {"LCD_D20", "ETXD1"}, 96 | "PD21": {"LCD_D21", "ETXD2"}, 97 | "PD22": {"LCD_D22", "ETXD3"}, 98 | "PD23": {"LCD_D23", "ETXEN"}, 99 | "PD24": {"LCD_CLK", "ETXCK"}, 100 | "PD25": {"LCD_DE", "ETXERR"}, 101 | "PD26": {"LCD_HSYNC", "EMDC"}, 102 | "PD27": {"LCD_VSYNC", "EMDIO"}, 103 | "PE0": {"TS_CLK", "CSI_PCLK", "SPI2_CS0", "", "EINT14"}, 104 | "PE1": {"TS_ERR", "CSI_MCLK", "SPI2_CLK", "", "EINT15"}, 105 | "PE2": {"TS_SYNC", "CSI_HSYNC", "SPI2_MOSI"}, 106 | "PE3": {"TS_DVLD", "CSI_VSYNC", "SPI2_MISO"}, 107 | "PE4": {"TS_D0", "CSI_D0", "SDC2_D0"}, 108 | "PE5": {"TS_D1", "CSI_D1", "SDC2_D1"}, 109 | "PE6": {"TS_D2", "CSI_D2", "SDC2_D2"}, 110 | "PE7": {"TS_D3", "CSI_D3", "SDC2_D3"}, 111 | "PE8": {"TS_D4", "CSI_D4", "SDC2_CMD"}, 112 | "PE9": {"TS_D5", "CSI_D5", "SDC2_CLK"}, 113 | "PE10": {"TS_D6", "CSI_D6", "UART1_TX"}, 114 | "PE11": {"TS_D7", "CSI_D7", "UART1_RX"}, 115 | "PF0": {"SDC0_D1", "", "JTAG1_TMS"}, 116 | "PF1": {"SDC0_D0", "", "JTAG1_TDI"}, 117 | "PF2": {"SDC0_CLK", "", "UART0_TX"}, 118 | "PF3": {"SDC0_CMD", "", "JTAG1_TDO"}, 119 | "PF4": {"SDC0_D3", "", "UART0_RX"}, 120 | "PF5": {"SDC0_D2", "", "JTAG1_TCK"}, 121 | "PG0": {"GPS_CLK", "", "", "", "EINT0"}, 122 | "PG1": {"GPS_SIGN", "", "", "", "EINT1"}, 123 | "PG2": {"GPS_MAG", "", "", "", "EINT2"}, 124 | "PG3": {"", "", "UART1_TX", "", "EINT3"}, 125 | "PG4": {"", "", "UART1_RX", "", "EINT4"}, 126 | "PG9": {"SPI1_CS0", "UART3_TX", "", "", "EINT9"}, 127 | "PG10": {"SPI1_CLK", "UART3_RX", "", "", "EINT10"}, 128 | "PG11": {"SPI1_MOSI", "UART3_CTS", "", "", "EINT11"}, 129 | "PG12": {"SPI1_MISO", "UART3_RTS", "", "", "EINT12"}, 130 | } 131 | 132 | // mapR8Pins uses mappingR8 to actually set the altFunc fields of all gpio and 133 | // mark them as available. 134 | // 135 | // It is called by the generic allwinner processor code if a R8 is detected. 136 | func mapR8Pins() error { 137 | for name, altFuncs := range mappingR8 { 138 | pin := cpupins[name] 139 | pin.altFunc = altFuncs 140 | pin.available = true 141 | if strings.Contains(string(altFuncs[4]), "EINT") { 142 | pin.supportEdge = true 143 | } 144 | 145 | // Initializes the sysfs corresponding pin right away. 146 | pin.sysfsPin = sysfs.Pins[pin.Number()] 147 | } 148 | return nil 149 | } 150 | -------------------------------------------------------------------------------- /am335x/am335x.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package am335x 6 | 7 | import ( 8 | "errors" 9 | "strings" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/host/v3/distro" 13 | ) 14 | 15 | // Present returns true if a TM AM335x processor is detected. 16 | func Present() bool { 17 | if isArm { 18 | return strings.HasPrefix(distro.DTModel(), "TI AM335x") 19 | } 20 | return false 21 | } 22 | 23 | // driver implements periph.Driver. 24 | type driver struct { 25 | } 26 | 27 | func (d *driver) String() string { 28 | return "am335x" 29 | } 30 | 31 | func (d *driver) Prerequisites() []string { 32 | return nil 33 | } 34 | 35 | func (d *driver) After() []string { 36 | return nil 37 | } 38 | 39 | func (d *driver) Init() (bool, error) { 40 | if !Present() { 41 | return false, errors.New("am335x CPU not detected") 42 | } 43 | return true, nil 44 | } 45 | 46 | func init() { 47 | if isArm { 48 | driverreg.MustRegister(&drv) 49 | } 50 | } 51 | 52 | var drv driver 53 | -------------------------------------------------------------------------------- /am335x/am335x_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package am335x 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /am335x/am335x_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package am335x 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /am335x/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package am335x exposes functionality for the Texas Instruments Sitara AM335x 6 | // processor family. 7 | // 8 | // This processor family is found on the BeagleBone. PRU-ICSS functionality is 9 | // implemented in package pru. 10 | // 11 | // The GPIO pins of the AM335x CPU are grouped into 3 groups of 32 pins: GPIO0, 12 | // GPIO1, and GPIO2. The CPU documentation refers to GPIO in the form of 13 | // GPIOx_y. To get the absolute number, as exposed by sysfs, use 32*x+y to get 14 | // the absolute number. 15 | // 16 | // # Datasheet 17 | // 18 | // Technical Reference Manual 19 | // https://www.ti.com/lit/ug/spruh73p/spruh73p.pdf 20 | // 21 | // # Other 22 | // 23 | // Marketing page 24 | // https://www.ti.com/processors/sitara/arm-cortex-a8/am335x/overview.html 25 | // 26 | // Family overview 27 | // https://www.ti.com/lit/ds/symlink/am3359.pdf 28 | package am335x 29 | -------------------------------------------------------------------------------- /bcm283x/bcm283x_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /bcm283x/bcm283x_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build arm64 6 | // +build arm64 7 | 8 | package bcm283x 9 | 10 | const isArm = true 11 | -------------------------------------------------------------------------------- /bcm283x/bcm283x_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm && !arm64 6 | // +build !arm,!arm64 7 | 8 | package bcm283x 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /bcm283x/bcm283x_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | import "periph.io/x/host/v3/fs" 8 | 9 | func init() { 10 | fs.Inhibit() 11 | } 12 | -------------------------------------------------------------------------------- /bcm283x/bcm283xsmoketest/benchmark.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283xsmoketest 6 | 7 | import ( 8 | "errors" 9 | "flag" 10 | "fmt" 11 | 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpioreg" 14 | "periph.io/x/host/v3/bcm283x" 15 | ) 16 | 17 | // Benchmark is imported by periph-smoketest. 18 | type Benchmark struct { 19 | short bool 20 | p *bcm283x.Pin 21 | pull gpio.Pull 22 | } 23 | 24 | // Name implements the SmokeTest interface. 25 | func (s *Benchmark) Name() string { 26 | return "bcm283x-benchmark" 27 | } 28 | 29 | // Description implements the SmokeTest interface. 30 | func (s *Benchmark) Description() string { 31 | return "Benchmarks bcm283x functionality" 32 | } 33 | 34 | // Run implements the SmokeTest interface. 35 | func (s *Benchmark) Run(f *flag.FlagSet, args []string) error { 36 | name := f.String("p", "", "Pin to use") 37 | f.BoolVar(&s.short, "short", false, "Skip many partially redundant benchmarks") 38 | if err := f.Parse(args); err != nil { 39 | return err 40 | } 41 | 42 | if f.NArg() != 0 { 43 | f.Usage() 44 | return errors.New("unsupported flags") 45 | } 46 | if !bcm283x.Present() { 47 | f.Usage() 48 | return errors.New("this smoke test can only be run on a bcm283x based host") 49 | } 50 | if *name == "" { 51 | f.Usage() 52 | return errors.New("-p is required") 53 | } 54 | p := gpioreg.ByName(*name) 55 | if p == nil { 56 | return fmt.Errorf("invalid pin %q", *name) 57 | } 58 | if r, ok := p.(gpio.RealPin); ok { 59 | p = r.Real() 60 | } 61 | var ok bool 62 | if s.p, ok = p.(*bcm283x.Pin); !ok { 63 | return fmt.Errorf("pin is not bcm283x %q", *name) 64 | } 65 | s.pull = gpio.PullDown 66 | s.runFastGPIOBenchmark() 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /bcm283x/bcm283xsmoketest/benchmark_gpio_support_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283xsmoketest 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | func TestToHz(t *testing.T) { 15 | data := []struct { 16 | N int 17 | T time.Duration 18 | expected physic.Frequency 19 | }{ 20 | { 21 | 0, 22 | time.Second, 23 | 0, 24 | }, 25 | { 26 | 1, 27 | 0, 28 | 0, 29 | }, 30 | { 31 | 1, 32 | time.Millisecond, 33 | physic.KiloHertz, 34 | }, 35 | { 36 | 1, 37 | time.Second, 38 | physic.Hertz, 39 | }, 40 | { 41 | 3, 42 | 7 * time.Millisecond, 43 | // 3/7 with perfect rounding. 44 | 428571429 * physic.MicroHertz, 45 | }, 46 | { 47 | 3000, 48 | 7 * time.Microsecond, 49 | // 3/7 with perfect rounding. 50 | 428571428571429 * physic.MicroHertz, 51 | }, 52 | { 53 | 1000000, 54 | 1000 * time.Second, 55 | physic.KiloHertz, 56 | }, 57 | { 58 | 1000000, 59 | time.Second, 60 | physic.MegaHertz, 61 | }, 62 | { 63 | 1000000, 64 | time.Millisecond, 65 | physic.GigaHertz, 66 | }, 67 | { 68 | 1000000000, 69 | 1000 * time.Second, 70 | physic.MegaHertz, 71 | }, 72 | { 73 | 1000000000, 74 | time.Second, 75 | physic.GigaHertz, 76 | }, 77 | { 78 | 1234556000, 79 | // 2.3s. 80 | 2345567891 * time.Nanosecond, 81 | // 10 digits of resolution for 526.336MHz. 82 | 526335849711 * physic.MilliHertz, 83 | }, 84 | { 85 | 1000000000, 86 | time.Millisecond, 87 | physic.TeraHertz, 88 | }, 89 | { 90 | 300000000, 91 | 7 * time.Millisecond, 92 | // 3/7 with pretty good rounding, keeping in mind that's 42.857GHz. 93 | 42857142857143 * physic.MilliHertz, 94 | }, 95 | } 96 | for i, line := range data { 97 | r := testing.BenchmarkResult{N: line.N, T: line.T} 98 | if actual := toHz(&r); actual != line.expected { 99 | t.Fatalf("#%d: toHz(%d, %s) = %s(%d); expected %s(%d)", i, line.N, line.T, actual, actual, line.expected, line.expected) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /bcm283x/dma_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "periph.io/x/conn/v3/gpio/gpiostream" 12 | "periph.io/x/conn/v3/physic" 13 | ) 14 | 15 | func TestDmaStatus_String(t *testing.T) { 16 | if s := dmaStatus(0).String(); s != "0" { 17 | t.Fatal(s) 18 | } 19 | d := ^dmaStatus(0) 20 | if s := d.String(); s != "Reset|Abort|DisableDebug|WaitForOutstandingWrites|ErrorStatus|WaitingForOutstandingWrites|DreqStopsDMA|Paused|Dreq|Interrupt|End|Active|pp15|p15|dmaStatus(0xf00fe80)" { 21 | t.Fatal(s) 22 | } 23 | } 24 | 25 | func TestDmaTransferInfo_String(t *testing.T) { 26 | if s := dmaTransferInfo(0).String(); s != "Fire" { 27 | t.Fatal(s) 28 | } 29 | d := ^dmaTransferInfo(0) 30 | if s := d.String(); s != "NoWideBursts|SrcIgnore|SrcDReq|SrcWidth128|SrcInc|DstIgnore|DstDReq|DstWidth128|DstInc|WaitResp|Transfer2DMode|InterruptEnable|waits=31|burst=15|SlimBusDC9|dmaTransferInfo(0xf8000004)" { 31 | t.Fatal(s) 32 | } 33 | } 34 | 35 | func TestDmaDebug_String(t *testing.T) { 36 | if s := dmaDebug(0).String(); s != "0" { 37 | t.Fatal(s) 38 | } 39 | d := ^dmaDebug(0) 40 | if s := d.String(); s != "Lite|ReadError|FIFOError|ReadLastNotSetError|v7|state(1ff)|#ff|OutstandingWrites=15|dmaDebug(0xe0000008)" { 41 | t.Fatal(s) 42 | } 43 | } 44 | 45 | func TestDmaStride_String(t *testing.T) { 46 | if s := dmaStride(0).String(); s != "0x0" { 47 | t.Fatal(s) 48 | } 49 | d := ^dmaStride(0) 50 | if s := d.String(); s != "0xffff,0xffff" { 51 | t.Fatal(s) 52 | } 53 | } 54 | 55 | func TestControlBlock(t *testing.T) { 56 | c := controlBlock{} 57 | if c.initBlock(0, 0, 0, true, true, false, false, dmaFire) == nil { 58 | t.Fatal("can't set both") 59 | } 60 | if c.initBlock(0, 0, 0, false, false, true, true, dmaFire) == nil { 61 | t.Fatal("need at least one addr") 62 | } 63 | if c.initBlock(0, 1, 0, true, false, false, true, dmaFire) == nil { 64 | t.Fatal("srcIO requires srcAddr") 65 | } 66 | if c.initBlock(1, 0, 0, false, true, true, false, dmaFire) == nil { 67 | t.Fatal("dstIO requires dstAddr") 68 | } 69 | if c.initBlock(1, 1, 0, false, false, true, true, dmaSrcIgnore) == nil { 70 | t.Fatal("must not specify anything other than clock source") 71 | } 72 | 73 | if err := c.initBlock(1, 0, 0, false, false, true, true, dmaFire); err != nil { 74 | t.Fatal(err) 75 | } 76 | if err := c.initBlock(0, 1, 0, false, false, true, true, dmaFire); err != nil { 77 | t.Fatal(err) 78 | } 79 | if err := c.initBlock(1, 0, 0, true, false, false, true, dmaFire); err != nil { 80 | t.Fatal(err) 81 | } 82 | if err := c.initBlock(0, 1, 0, false, true, true, false, dmaPCMTX); err != nil { 83 | t.Fatal(err) 84 | } 85 | } 86 | 87 | func TestControlBlockGo_String(t *testing.T) { 88 | c := controlBlock{} 89 | if err := c.initBlock(0, 1, 0, false, true, false, false, dmaPCMTX); err != nil { 90 | t.Fatal(err) 91 | } 92 | expected := "{\n transferInfo: NoWideBursts|SrcIgnore|DstDReq|WaitResp|waits=1|PCMTX,\n srcAddr: 0x0,\n dstAddr: 0x7e000001,\n txLen: 0,\n stride: 0x0,\n nextCB: 0x0,\n}" 93 | if s := c.GoString(); s != expected { 94 | t.Fatalf("%q", s) 95 | } 96 | } 97 | 98 | func TestDmaChannel(t *testing.T) { 99 | d := dmaChannel{} 100 | if !d.isAvailable() { 101 | t.Fatal("empty channel is available") 102 | } 103 | d = dmaChannel{cs: dmaEnd} 104 | if err := d.wait(); err != nil { 105 | t.Fatal(err) 106 | } 107 | d = dmaChannel{debug: dmaReadError} 108 | if d.wait() == nil { 109 | t.Fatal("read error") 110 | } 111 | d = dmaChannel{debug: dmaFIFOError} 112 | if d.wait() == nil { 113 | t.Fatal("fifo error") 114 | } 115 | d = dmaChannel{debug: dmaReadLastNotSetError} 116 | if d.wait() == nil { 117 | t.Fatal("read last not set error") 118 | } 119 | } 120 | 121 | func TestDmaChannel_GoString(t *testing.T) { 122 | d := dmaChannel{} 123 | d.reset() 124 | d.startIO(0) 125 | expected := "{\n cs: WaitForOutstandingWrites|Active|pp8|p8,\n cbAddr: 0x0,\n transferInfo: Fire,\n srcAddr: 0x0,\n dstAddr: 0x0,\n txLen: 0,\n stride: 0x0,\n nextCB: 0x0,\n debug: 0,\n reserved: {...},\n}" 126 | if s := d.GoString(); s != expected { 127 | t.Fatalf("%q", s) 128 | } 129 | } 130 | 131 | func TestDmaMap_GoString(t *testing.T) { 132 | d := dmaMap{} 133 | // I have to admit, this is the worst test ever. 134 | if s := d.GoString(); len(s) != 3629 { 135 | t.Fatal(s, len(s)) 136 | } 137 | } 138 | 139 | func TestStructSizes(t *testing.T) { 140 | // Verify internal assumptions. 141 | if s := reflect.TypeOf((*controlBlock)(nil)).Elem().Size(); s != 256/8 { 142 | t.Fatalf("controlBlock size: %d", s) 143 | } 144 | if s := reflect.TypeOf((*dmaChannel)(nil)).Elem().Size(); s != 0x100 { 145 | t.Fatalf("dmaChannel size: %d", s) 146 | } 147 | } 148 | 149 | func TestCopyStreamToDMAbuf(t *testing.T) { 150 | buf := make([]uint32, 2) 151 | stream := gpiostream.BitStream{ 152 | Bits: []byte{1, 2, 3, 4, 5, 6, 7}, 153 | Freq: physic.KiloHertz, 154 | LSBF: false, 155 | } 156 | if err := copyStreamToDMABuf(&stream, buf); err != nil { 157 | t.Fatal(err) 158 | } 159 | if buf[0] != 0x01020304 { 160 | t.Fatalf("Unexpected 0x%x != 0x%x", buf[0], 0x01020304) 161 | } 162 | if buf[1] != 0x05060700 { 163 | t.Fatalf("Unexpected 0x%x != 0x%x", buf[1], 0x05060700) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /bcm283x/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package bcm283x exposes the BCM283x GPIO functionality. 6 | // 7 | // This driver implements memory-mapped GPIO pin manipulation and leverages 8 | // ioctl-gpio for edge detection. 9 | // 10 | // If you are looking for the actual implementation, open doc.go for further 11 | // implementation details. 12 | // 13 | // # GPIOs 14 | // 15 | // Aliases for GPCLK0, GPCLK1, GPCLK2 are created for corresponding CLKn pins. 16 | // Same for PWM0_OUT and PWM1_OUT, which point respectively to PWM0 and PWM1. 17 | // 18 | // For multi-pin IO, you should prefer using the /host/gpioioctl/GPIOChip.LineSet() 19 | // functionality. It's chipset agnostic because it uses the ioctl interfaces, 20 | // and it offers multi-pin WaitForEdge(). 21 | // 22 | // # Datasheet 23 | // 24 | // https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 25 | // 26 | // Its crowd-sourced errata: http://elinux.org/BCM2835_datasheet_errata 27 | // 28 | // BCM2836: 29 | // https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf 30 | // 31 | // Another doc about PCM and PWM: 32 | // https://scribd.com/doc/127599939/BCM2835-Audio-clocks 33 | // 34 | // GPIO pad control: 35 | // https://scribd.com/doc/101830961/GPIO-Pads-Control2 36 | package bcm283x 37 | 38 | // Other implementations details 39 | // 40 | // mainline: 41 | // https://github.com/torvalds/linux/blob/master/drivers/dma/bcm2835-dma.c 42 | // https://github.com/torvalds/linux/blob/master/drivers/gpio 43 | // 44 | // Raspbian kernel: 45 | // https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/dma 46 | // https://github.com/raspberrypi/linux/blob/rpi-4.11.y/drivers/gpio 47 | -------------------------------------------------------------------------------- /bcm283x/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/physic" 12 | "periph.io/x/host/v3" 13 | "periph.io/x/host/v3/bcm283x" 14 | ) 15 | 16 | func ExamplePinsRead0To31() { 17 | // Make sure periph is initialized. 18 | if _, err := host.Init(); err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | // Print out the state of 32 GPIOs with a single read that reads all these 23 | // pins all at once. 24 | bits := bcm283x.PinsRead0To31() 25 | fmt.Printf("bits: %#x\n", bits) 26 | suffixes := []string{" ", "\n"} 27 | for i := uint(0); i < 32; i++ { 28 | fmt.Printf("GPIO%-2d: %d%s", i, (bits>>i)&1, suffixes[(i%4)/3]) 29 | } 30 | // Output: 31 | // bits: 0x80011010 32 | // GPIO0 : 0 GPIO1 : 0 GPIO2 : 0 GPIO3 : 0 33 | // GPIO4 : 1 GPIO5 : 0 GPIO6 : 0 GPIO7 : 0 34 | // GPIO8 : 0 GPIO9 : 0 GPIO10: 0 GPIO11: 0 35 | // GPIO12: 1 GPIO13: 0 GPIO14: 0 GPIO15: 0 36 | // GPIO16: 1 GPIO17: 0 GPIO18: 0 GPIO19: 0 37 | // GPIO20: 0 GPIO21: 0 GPIO22: 0 GPIO23: 0 38 | // GPIO24: 0 GPIO25: 0 GPIO26: 0 GPIO27: 0 39 | // GPIO28: 0 GPIO29: 0 GPIO30: 0 GPIO31: 1 40 | } 41 | 42 | func ExamplePinsClear0To31() { 43 | // Make sure periph is initialized. 44 | if _, err := host.Init(); err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | // Simultaneously clears GPIO4 and GPIO16 to gpio.Low. 49 | bcm283x.PinsClear0To31(1<<16 | 1<<4) 50 | } 51 | 52 | func ExamplePinsSet0To31() { 53 | // Make sure periph is initialized. 54 | if _, err := host.Init(); err != nil { 55 | log.Fatal(err) 56 | } 57 | 58 | // Simultaneously sets GPIO4 and GPIO16 to gpio.High. 59 | bcm283x.PinsClear0To31(1<<16 | 1<<4) 60 | } 61 | 62 | func ExamplePinsSetup0To27() { 63 | if err := bcm283x.PinsSetup0To27(16*physic.MilliAmpere, true, true); err != nil { 64 | log.Fatal(err) 65 | } 66 | fmt.Printf("drive: %s", bcm283x.GPIO0.Drive()) 67 | fmt.Printf("slew: %t", bcm283x.GPIO0.SlewLimit()) 68 | fmt.Printf("hysteresis: %t", bcm283x.GPIO0.Hysteresis()) 69 | } 70 | -------------------------------------------------------------------------------- /bcm283x/pwm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/conn/v3/physic" 11 | ) 12 | 13 | func TestPWMMap(t *testing.T) { 14 | defer reset() 15 | p := pwmMap{} 16 | p.reset() 17 | if _, err := setPWMClockSource(); err == nil { 18 | t.Fatal("pwmMemory is nil") 19 | } 20 | drvDMA.pwmMemory = &p 21 | if _, err := setPWMClockSource(); err == nil { 22 | t.Fatal("clockMemory is nil") 23 | } 24 | drvDMA.clockMemory = &clockMap{} 25 | drvDMA.pwmBaseFreq = 25 * physic.MegaHertz 26 | drvDMA.pwmDMAFreq = 200 * physic.KiloHertz 27 | if _, err := setPWMClockSource(); err == nil { 28 | t.Fatal("can't write to clock register") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /bcm283x/streams.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | import ( 8 | "encoding/binary" 9 | "errors" 10 | "fmt" 11 | 12 | "periph.io/x/conn/v3/gpio/gpiostream" 13 | ) 14 | 15 | // uint32ToBitLSBF packs a bit offset found on slice `d` (that is actually 16 | // uint32) back into a densely packed Bits stream. 17 | func uint32ToBitLSBF(w []byte, d []uint8, bit uint8, skip int) { 18 | // Little endian. 19 | x := bit / 8 20 | d = d[x:] 21 | bit -= 8 * x 22 | mask := uint8(1) << bit 23 | for i := range w { 24 | w[i] = ((d[0]&mask)>>bit<<0 | 25 | (d[skip*1]&mask)>>bit<<1 | 26 | (d[skip*2]&mask)>>bit<<2 | 27 | (d[skip*3]&mask)>>bit<<3 | 28 | (d[skip*4]&mask)>>bit<<4 | 29 | (d[skip*5]&mask)>>bit<<5 | 30 | (d[skip*6]&mask)>>bit<<6 | 31 | (d[skip*7]&mask)>>bit<<7) 32 | d = d[skip*8:] 33 | } 34 | } 35 | 36 | func getBit(b byte, index int, msb bool) byte { 37 | var shift uint 38 | if msb { 39 | shift = uint(7 - index) 40 | } else { 41 | shift = uint(index) 42 | } 43 | return (b >> shift) & 1 44 | } 45 | 46 | func raster32Bits(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error { 47 | var msb bool 48 | var bits []byte 49 | switch b := s.(type) { 50 | case *gpiostream.BitStream: 51 | msb = !b.LSBF 52 | bits = b.Bits 53 | default: 54 | return fmt.Errorf("unsupported type %T", b) 55 | } 56 | m := len(clear) / 8 57 | if n := len(bits); n < m { 58 | m = n 59 | } 60 | index := 0 61 | for i := 0; i < m; i++ { 62 | for j := 0; j < 8; j++ { 63 | if getBit(bits[i], j, msb) != 0 { 64 | for k := 0; k < skip; k++ { 65 | set[index] |= mask 66 | index++ 67 | } 68 | } else { 69 | for k := 0; k < skip; k++ { 70 | clear[index] |= mask 71 | index++ 72 | } 73 | } 74 | } 75 | } 76 | return nil 77 | } 78 | 79 | // raster32 rasters the stream into a uint32 stream with the specified masks to 80 | // put in the correctly slice when the bit is set and when it is clear. 81 | // 82 | // `s` must be one of the types in this package. 83 | func raster32(s gpiostream.Stream, skip int, clear, set []uint32, mask uint32) error { 84 | if mask == 0 { 85 | return errors.New("bcm283x: mask is 0") 86 | } 87 | if len(clear) == 0 { 88 | return errors.New("bcm283x: clear buffer is empty") 89 | } 90 | if len(set) == 0 { 91 | return errors.New("bcm283x: set buffer is empty") 92 | } 93 | if len(clear) != len(set) { 94 | return errors.New("bcm283x: clear and set buffers have different length") 95 | } 96 | switch x := s.(type) { 97 | case *gpiostream.BitStream: 98 | // TODO 99 | return raster32Bits(x, skip, clear, set, mask) 100 | case *gpiostream.EdgeStream: 101 | return errors.New("bcm283x: EdgeStream is not supported yet") 102 | case *gpiostream.Program: 103 | return errors.New("bcm283x: Program is not supported yet") 104 | default: 105 | return errors.New("bcm283x: unknown stream type") 106 | } 107 | } 108 | 109 | // PCM/PWM DMA buf is encoded as little-endian and MSB first. 110 | func copyStreamToDMABuf(w gpiostream.Stream, dst []uint32) error { 111 | switch v := w.(type) { 112 | case *gpiostream.BitStream: 113 | if v.LSBF { 114 | return errors.New("TODO(simokawa): handle BitStream.LSBF") 115 | } 116 | // This is big-endian and MSB first. 117 | i := 0 118 | for ; i < len(v.Bits)/4; i++ { 119 | dst[i] = binary.BigEndian.Uint32(v.Bits[i*4:]) 120 | } 121 | last := uint32(0) 122 | if mod := len(v.Bits) % 4; mod > 0 { 123 | for j := 0; j < mod; j++ { 124 | last |= (uint32(v.Bits[i*4+j])) << uint32(8*(3-j)) 125 | } 126 | dst[i] = last 127 | } 128 | return nil 129 | case *gpiostream.EdgeStream: 130 | return errors.New("TODO(simokawa): handle EdgeStream") 131 | default: 132 | return errors.New("unsupported Stream type") 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /bcm283x/streams_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "periph.io/x/conn/v3/gpio/gpiostream" 12 | "periph.io/x/conn/v3/physic" 13 | ) 14 | 15 | func TestRaster32Bits(t *testing.T) { 16 | b := gpiostream.BitStream{ 17 | Bits: []byte{0x1, 0x40}, 18 | Freq: physic.Hertz, 19 | LSBF: true, 20 | } 21 | d32Clear := make([]uint32, 8*2) 22 | d32Set := make([]uint32, 8*2) 23 | if err := raster32Bits(&b, 1, d32Clear, d32Set, 2); err != nil { 24 | t.Fatal(err) 25 | } 26 | if !reflect.DeepEqual(d32Set, []uint32{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0}) { 27 | t.Errorf("unexpected d32Set %v", d32Set) 28 | } 29 | if !reflect.DeepEqual(d32Clear, []uint32{0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2}) { 30 | t.Errorf("unexpected d32Clear %v", d32Clear) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bcm283x/timer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | import ( 8 | "sync/atomic" 9 | "time" 10 | 11 | "periph.io/x/host/v3/cpu" 12 | ) 13 | 14 | // ReadTime returns the time on a monotonic 1Mhz clock (1µs resolution). 15 | // 16 | // It only works if bcm283x-dma successfully loaded. Otherwise it returns 0. 17 | func ReadTime() time.Duration { 18 | if drvDMA.timerMemory == nil { 19 | return 0 20 | } 21 | // Use atomic package to safely access memory 22 | high := atomic.LoadUint32(&drvDMA.timerMemory.high) 23 | low := atomic.LoadUint32(&drvDMA.timerMemory.low) 24 | return (time.Duration(high)<<32 | time.Duration(low)) * time.Microsecond 25 | } 26 | 27 | // Nanospin spins the CPU without calling into the kernel code if possible. 28 | func Nanospin(t time.Duration) { 29 | start := ReadTime() 30 | if start == 0 { 31 | // Use the slow generic version. 32 | cpu.Nanospin(t) 33 | return 34 | } 35 | // TODO(maruel): Optimize code path for sub-1µs duration. 36 | for ReadTime()-start < t { 37 | } 38 | } 39 | 40 | // 41 | 42 | const ( 43 | // 31:4 reserved 44 | timerM3 = 1 << 3 // M3 45 | timerM2 = 1 << 2 // M2 46 | timerM1 = 1 << 1 // M1 47 | timerM0 = 1 << 0 // M0 48 | ) 49 | 50 | // Page 173 51 | type timerCtl uint32 52 | 53 | // timerMap represents the registers to access the 1Mhz timer. 54 | // 55 | // Page 172 56 | type timerMap struct { 57 | ctl timerCtl // CS 58 | low uint32 // CLO 59 | high uint32 // CHI 60 | c0 uint32 // 0 61 | c1 uint32 // C1 62 | c2 uint32 // C2 63 | c3 uint32 // C3 64 | } 65 | -------------------------------------------------------------------------------- /bcm283x/timer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bcm283x 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestReadTime(t *testing.T) { 13 | if ReadTime() != 0 { 14 | t.Fatal("timerMemory is nil") 15 | } 16 | 17 | defer func() { 18 | drvDMA.timerMemory = nil 19 | }() 20 | drvDMA.timerMemory = &timerMap{low: 1} 21 | if d := ReadTime(); d != time.Microsecond { 22 | t.Fatal(d) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /beagle/beagle.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package beagle 6 | 7 | import ( 8 | "strings" 9 | 10 | "periph.io/x/host/v3/distro" 11 | ) 12 | 13 | // Present returns true if the host is a BeagleBone. 14 | func Present() bool { 15 | if isArm { 16 | return strings.HasPrefix(distro.DTModel(), "TI AM335x BeagleBone") 17 | } 18 | return false 19 | } 20 | -------------------------------------------------------------------------------- /beagle/beagle_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package beagle 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /beagle/beagle_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package beagle 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /beagle/black/black.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package black implements headers for the BeagleBone Black and BeagleBone 6 | // Black Wireless micro-computers. 7 | // 8 | // # Reference 9 | // 10 | // https://beagleboard.org/black 11 | // 12 | // # Datasheet 13 | // 14 | // https://elinux.org/Beagleboard:BeagleBoneBlack 15 | // 16 | // https://github.com/CircuitCo/BeagleBone-Black/blob/rev_b/BBB_SRM.pdf 17 | // 18 | // https://elinux.org/Beagleboard:Cape_Expansion_Headers 19 | package black 20 | 21 | import ( 22 | "strings" 23 | 24 | "periph.io/x/host/v3/distro" 25 | ) 26 | 27 | // Present returns true if the host is a BeagleBone Black or BeagleBone Black 28 | // Wireless. 29 | func Present() bool { 30 | if isArm { 31 | return strings.HasPrefix(distro.DTModel(), "TI AM335x BeagleBone Black") 32 | } 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /beagle/black/black_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package black 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /beagle/black/black_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package black 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /beagle/bone/bone_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package bone 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /beagle/bone/bone_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package bone 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /beagle/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package beagle regroups subpackages containing BeagleBoard/BeagleBone board 6 | // family headers definition. 7 | package beagle 8 | -------------------------------------------------------------------------------- /beagle/green/green.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package green implements headers for the BeagleBone Green and BeagleBone 6 | // Green Wireless micro-computers. 7 | // 8 | // # Reference 9 | // 10 | // https://beagleboard.org/green 11 | // 12 | // https://beagleboard.org/green-wireless 13 | // 14 | // # Datasheet 15 | // 16 | // http://wiki.seeedstudio.com/BeagleBone_Green/ 17 | package green 18 | 19 | import ( 20 | "errors" 21 | "strings" 22 | 23 | "periph.io/x/conn/v3/driver/driverreg" 24 | "periph.io/x/conn/v3/gpio" 25 | "periph.io/x/conn/v3/pin" 26 | "periph.io/x/conn/v3/pin/pinreg" 27 | "periph.io/x/host/v3/distro" 28 | "periph.io/x/host/v3/sysfs" 29 | ) 30 | 31 | // Headers found on BeagleBone Green. 32 | var ( 33 | // I2C Groove port. 34 | I2C_SCL gpio.PinIO = gpio.INVALID // GPIO13, I2C2_SCL, UART1_RTS, SPI1_CS1 35 | I2C_SDA gpio.PinIO = gpio.INVALID // GPIO12, I2C2_SDA, UART1_CTS, SPI1_CS0 36 | 37 | // UART Groove port connected to UART2. 38 | UART_TX gpio.PinIO = gpio.INVALID // GPIO3, EHRPWM0B, I2C2_SCL, UART2_TX, SPI0_MISO 39 | UART_RX gpio.PinIO = gpio.INVALID // GPIO2, EHRPWM0A, I2C2_SDA, UART2_RX, SPI0_CLK 40 | ) 41 | 42 | // Present returns true if the host is a BeagleBone Green or BeagleBone Green 43 | // Wireless. 44 | func Present() bool { 45 | if isArm { 46 | return strings.HasPrefix(distro.DTModel(), "TI AM335x BeagleBone Green") 47 | } 48 | return false 49 | } 50 | 51 | // driver implements periph.Driver. 52 | type driver struct { 53 | } 54 | 55 | func (d *driver) String() string { 56 | return "beaglebone-green" 57 | } 58 | 59 | func (d *driver) Prerequisites() []string { 60 | return []string{"am335x", "sysfs-gpio"} 61 | } 62 | 63 | func (d *driver) After() []string { 64 | return nil 65 | } 66 | 67 | func (d *driver) Init() (bool, error) { 68 | if !Present() { 69 | return false, errors.New("BeagleBone Green board not detected") 70 | } 71 | 72 | I2C_SDA = sysfs.Pins[12] 73 | I2C_SCL = sysfs.Pins[13] 74 | hdr := [][]pin.Pin{{pin.GROUND}, {pin.V3_3}, {I2C_SDA}, {I2C_SCL}} 75 | if err := pinreg.Register("I2C", hdr); err != nil { 76 | return true, err 77 | } 78 | 79 | UART_TX = sysfs.Pins[3] 80 | UART_RX = sysfs.Pins[2] 81 | hdr = [][]pin.Pin{{pin.GROUND}, {pin.V3_3}, {UART_TX}, {UART_RX}} 82 | if err := pinreg.Register("UART", hdr); err != nil { 83 | return true, err 84 | } 85 | 86 | return true, nil 87 | } 88 | 89 | func init() { 90 | if isArm { 91 | driverreg.MustRegister(&drv) 92 | } 93 | } 94 | 95 | var drv driver 96 | -------------------------------------------------------------------------------- /beagle/green/green_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package green 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /beagle/green/green_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package green 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /chip/chip_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package chip 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /chip/chip_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package chip 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /chip/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package chip contains header definitions for NextThing Co's C.H.I.P. board. 6 | // 7 | // CHIP uses the Allwinner R8 processor and thus the allwinner host package is 8 | // automatically imported. 9 | // 10 | // This package exports the U13 header, which is opposite the power LED, and 11 | // U14, which is right next to the power LED. Most of the pins are usable as 12 | // GPIO and are directly to the processor. These can use memory-mapped GPIO, 13 | // which is very fast. The XIO-P0 through XIO-P7 pins are attached to a pcf8574 14 | // I²C expander which has the result that all accesses to these pins have to go 15 | // through the kernel and the I²C bus protocol, i.e., they're slow. 16 | // 17 | // GPIO edge detection (using interrupts) is only supported on a few of the 18 | // processor's pins: AP-EINT1, AP-EINT3, CSIPCK, and CSICK. Edge detection is 19 | // also supported on the XIO pins, but this feature is rather limited due to 20 | // the device and the driver (for example, the driver interrupts on all edges). 21 | // 22 | // # References 23 | // 24 | // http://www.chip-community.org/index.php/Hardware_Information 25 | // 26 | // http://docs.getchip.com/chip.html#chip-hardware 27 | // 28 | // A graphical view of the board headers is available at: 29 | // http://docs.getchip.com/chip.html#pin-headers 30 | package chip 31 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Periph Authors. All rights reserved. 2 | # Use of this source code is governed under the Apache License, Version 2.0 3 | # that can be found in the LICENSE file. 4 | 5 | # https://docs.codecov.io/docs/codecovyml-reference 6 | # and 7 | # https://docs.codecov.io/docs/coverage-configuration 8 | coverage: 9 | precision: 1 10 | range: "40...80" 11 | round: nearest 12 | status: 13 | patch: 14 | default: 15 | target: 60% 16 | threshold: 10% 17 | project: 18 | default: 19 | target: 60% 20 | threshold: 10% 21 | -------------------------------------------------------------------------------- /cpu/cpu.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package cpu 6 | 7 | import ( 8 | "io" 9 | "os" 10 | "strconv" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "periph.io/x/host/v3/fs" 16 | ) 17 | 18 | // MaxSpeed returns the processor maximum speed in Hz. 19 | // 20 | // Returns 0 if it couldn't be calculated. 21 | func MaxSpeed() int64 { 22 | if isLinux { 23 | return getMaxSpeedLinux() 24 | } 25 | return 0 26 | } 27 | 28 | // Nanospin spins for a short amount of time doing a busy loop. 29 | // 30 | // This function should be called with durations of 10µs or less. 31 | func Nanospin(d time.Duration) { 32 | // TODO(maruel): Use runtime.LockOSThread()? 33 | if isLinux { 34 | nanospinLinux(d) 35 | } else { 36 | nanospinTime(d) 37 | } 38 | } 39 | 40 | // 41 | 42 | var ( 43 | mu sync.Mutex 44 | maxSpeed int64 = -1 45 | openFile = openFileOrig 46 | ) 47 | 48 | func openFileOrig(path string, flag int) (io.ReadCloser, error) { 49 | f, err := fs.Open(path, flag) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return f, nil 54 | } 55 | 56 | func getMaxSpeedLinux() int64 { 57 | mu.Lock() 58 | defer mu.Unlock() 59 | if maxSpeed == -1 { 60 | maxSpeed = 0 61 | if f, err := openFile("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", os.O_RDONLY); err == nil { 62 | defer f.Close() //#nosec G307 -- File is opened readonly so errors on file close cannot cause write errors 63 | if b, err := io.ReadAll(f); err == nil { 64 | s := strings.TrimSpace(string(b)) 65 | if i, err := strconv.ParseInt(s, 10, 64); err == nil { 66 | // Weirdly, the speed is listed as khz. :( 67 | maxSpeed = i * 1000 68 | } 69 | } 70 | } 71 | } 72 | return maxSpeed 73 | } 74 | 75 | func nanospinTime(d time.Duration) { 76 | // TODO(maruel): That's not optimal; it's actually pretty bad. 77 | // time.Sleep() sleeps for really too long, calling it repeatedly with 78 | // minimal value will give the caller a wake rate of 5KHz or so, depending on 79 | // the host. This makes it useless for bitbanging protocol implementations. 80 | for start := time.Now(); time.Since(start) < d; { 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /cpu/cpu_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package cpu 6 | 7 | import ( 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | const isLinux = true 13 | 14 | func nanospinLinux(d time.Duration) { 15 | // runtime.nanotime() is not exported so it cannot be used to busy loop for 16 | // very short sleep (10µs or less). 17 | time := syscall.NsecToTimespec(d.Nanoseconds()) 18 | leftover := syscall.Timespec{} 19 | for syscall.Nanosleep(&time, &leftover) != nil { 20 | time = leftover 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cpu/cpu_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !linux 6 | // +build !linux 7 | 8 | package cpu 9 | 10 | import "time" 11 | 12 | const isLinux = false 13 | 14 | func nanospinLinux(d time.Duration) { 15 | } 16 | -------------------------------------------------------------------------------- /cpu/cpu_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package cpu 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "os" 11 | "testing" 12 | "time" 13 | 14 | "periph.io/x/host/v3/fs" 15 | ) 16 | 17 | func TestMaxSpeed_fail(t *testing.T) { 18 | defer reset() 19 | if s := MaxSpeed(); s != 0 { 20 | t.Fatal(s) 21 | } 22 | } 23 | 24 | func TestMaxSpeed(t *testing.T) { 25 | defer reset() 26 | openFile = func(path string, flag int) (io.ReadCloser, error) { 27 | if path != "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" { 28 | t.Fatal(path) 29 | } 30 | if flag != os.O_RDONLY { 31 | t.Fatal(flag) 32 | } 33 | return io.NopCloser(bytes.NewBufferString("1001\n")), nil 34 | } 35 | MaxSpeed() 36 | if s := getMaxSpeedLinux(); s != 1001000 { 37 | t.Fatal(s) 38 | } 39 | } 40 | 41 | func TestNanospin(t *testing.T) { 42 | Nanospin(time.Microsecond) 43 | nanospinTime(time.Microsecond) 44 | } 45 | 46 | // 47 | 48 | func init() { 49 | fs.Inhibit() 50 | } 51 | 52 | func reset() { 53 | openFile = openFileOrig 54 | maxSpeed = -1 55 | } 56 | -------------------------------------------------------------------------------- /cpu/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package cpu implements functions relating to the host CPU itself. 6 | package cpu 7 | -------------------------------------------------------------------------------- /distro/devtree.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package distro 6 | 7 | import ( 8 | "encoding/binary" 9 | "os" 10 | ) 11 | 12 | // DTModel returns platform model info from the Linux device tree (/proc/device-tree/model), and 13 | // returns "unknown" on non-linux systems or if the file is missing. 14 | func DTModel() string { 15 | mu.Lock() 16 | defer mu.Unlock() 17 | 18 | if dtModel == "" { 19 | dtModel = "" 20 | if isLinux { 21 | dtModel = makeDTModelLinux() 22 | } 23 | } 24 | return dtModel 25 | } 26 | 27 | // DTCompatible returns platform compatibility info from the Linux device tree 28 | // (/proc/device-tree/compatible), and returns []{"unknown"} on non-linux systems or if the file is 29 | // missing. 30 | func DTCompatible() []string { 31 | mu.Lock() 32 | defer mu.Unlock() 33 | 34 | if dtCompatible == nil { 35 | dtCompatible = []string{} 36 | if isLinux { 37 | dtCompatible = makeDTCompatible() 38 | } 39 | } 40 | return dtCompatible 41 | } 42 | 43 | // DTRevision returns the device revision (e.g. a02082 for the Raspberry Pi 3) 44 | // from the Linux device tree, or 0 if the file is missing or malformed. 45 | func DTRevision() uint32 { 46 | mu.Lock() 47 | defer mu.Unlock() 48 | 49 | if dtRevisionRead { 50 | return dtRevision 51 | } 52 | dtRevisionRead = true 53 | if b, _ := os.ReadFile("/proc/device-tree/system/linux,revision"); len(b) >= 4 { 54 | dtRevision = binary.BigEndian.Uint32(b[:4]) 55 | } 56 | return dtRevision 57 | } 58 | 59 | // 60 | 61 | var ( 62 | dtModel string // cached /proc/device-tree/model 63 | dtCompatible []string // cached /proc/device-tree/compatible 64 | dtRevision uint32 // cached /proc/device-tree/system/linux,revision 65 | dtRevisionRead bool 66 | ) 67 | 68 | func makeDTModelLinux() string { 69 | // Read model from device tree. 70 | if bytes, err := readFile("/proc/device-tree/model"); err == nil { 71 | if model := splitNull(bytes); len(model) > 0 { 72 | return model[0] 73 | } 74 | } 75 | return "" 76 | } 77 | 78 | func makeDTCompatible() []string { 79 | // Read compatible from device tree. 80 | if bytes, err := readFile("/proc/device-tree/compatible"); err == nil { 81 | return splitNull(bytes) 82 | } 83 | return []string{} 84 | } 85 | -------------------------------------------------------------------------------- /distro/devtree_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package distro 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func TestDTModel_fail(t *testing.T) { 13 | defer reset() 14 | DTModel() 15 | } 16 | 17 | func TestDTModel(t *testing.T) { 18 | defer reset() 19 | readFile = func(filename string) ([]byte, error) { 20 | if filename != "/proc/device-tree/model" { 21 | t.Fatal(filename) 22 | } 23 | return []byte("SUPER-FOO\000"), nil 24 | } 25 | DTModel() 26 | if c := makeDTModelLinux(); c != "SUPER-FOO" { 27 | t.Fatal(c) 28 | } 29 | } 30 | 31 | func TestDTCompatible_fail(t *testing.T) { 32 | defer reset() 33 | DTCompatible() 34 | } 35 | 36 | func TestDTCompatible(t *testing.T) { 37 | defer reset() 38 | readFile = func(filename string) ([]byte, error) { 39 | if filename != "/proc/device-tree/compatible" { 40 | t.Fatal(filename) 41 | } 42 | return []byte("SUPER\000FOO\000"), nil 43 | } 44 | DTCompatible() 45 | if c := makeDTCompatible(); !reflect.DeepEqual(c, []string{"SUPER", "FOO"}) { 46 | t.Fatal(c) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /distro/distro.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package distro implements common functionality to auto-detect features on 6 | // the host; generally about linux distributions. 7 | // 8 | // Most of the functions exported as in the form IsFoo() where Foo is a linux 9 | // distribution. 10 | package distro 11 | 12 | import ( 13 | "os" 14 | "strconv" 15 | "strings" 16 | "sync" 17 | "unicode" 18 | ) 19 | 20 | // IsArmbian returns true if running on a Armbian distribution. 21 | // 22 | // http://www.armbian.com/ 23 | func IsArmbian() bool { 24 | if isArm && isLinux { 25 | // Armbian presents itself as debian in /etc/os-release so OSRelease() 26 | // cannot be used.. 27 | _, err := os.Stat("/etc/armbian.txt") 28 | return err == nil 29 | } 30 | return false 31 | } 32 | 33 | // IsDebian returns true if running on an Debian derived distribution. 34 | // 35 | // This function returns true on both Armbian, Raspbian and Ubuntu. 36 | // 37 | // https://debian.org/ 38 | func IsDebian() bool { 39 | if isLinux { 40 | // http://0pointer.de/public/systemd-man/os-release.html#ID_LIKE= 41 | if OSRelease()["ID"] == "debian" { 42 | return true 43 | } 44 | for _, part := range strings.Split(OSRelease()["ID_LIKE"], " ") { 45 | if part == "debian" { 46 | return true 47 | } 48 | } 49 | } 50 | return false 51 | } 52 | 53 | // IsRaspbian returns true if running on a Raspbian distribution. 54 | // 55 | // https://raspbian.org/ 56 | func IsRaspbian() bool { 57 | if isArm && isLinux { 58 | return OSRelease()["ID"] == "raspbian" 59 | } 60 | return false 61 | } 62 | 63 | // IsUbuntu returns true if running on an Ubuntu derived distribution. 64 | // 65 | // https://ubuntu.com/ 66 | func IsUbuntu() bool { 67 | if isLinux { 68 | return OSRelease()["ID"] == "ubuntu" 69 | } 70 | return false 71 | } 72 | 73 | // OSRelease returns parsed data from /etc/os-release. 74 | // 75 | // For more information, see 76 | // http://0pointer.de/public/systemd-man/os-release.html 77 | func OSRelease() map[string]string { 78 | if isLinux { 79 | return makeOSReleaseLinux() 80 | } 81 | return osRelease 82 | } 83 | 84 | // CPU 85 | 86 | // CPUInfo returns parsed data from /proc/cpuinfo. 87 | func CPUInfo() map[string]string { 88 | if isLinux { 89 | return makeCPUInfoLinux() 90 | } 91 | return cpuInfo 92 | } 93 | 94 | // 95 | 96 | var ( 97 | mu sync.Mutex 98 | cpuInfo map[string]string 99 | osRelease map[string]string 100 | readFile = os.ReadFile 101 | ) 102 | 103 | func splitSemiColon(content string) map[string]string { 104 | // Strictly speaking this format isn't ok, there can be multiple group. 105 | out := map[string]string{} 106 | for _, line := range strings.Split(content, "\n") { 107 | parts := strings.SplitN(line, ":", 2) 108 | if len(parts) != 2 { 109 | continue 110 | } 111 | // This format may have space around the ':'. 112 | key := strings.TrimRightFunc(parts[0], unicode.IsSpace) 113 | if len(key) == 0 || key[0] == '#' { 114 | continue 115 | } 116 | // Ignore duplicate keys. 117 | // TODO(maruel): Keep them all. 118 | if _, ok := out[key]; !ok { 119 | // Trim on both side, trailing space was observed on "Features" value. 120 | out[key] = strings.TrimFunc(parts[1], unicode.IsSpace) 121 | } 122 | } 123 | return out 124 | } 125 | 126 | func splitStrict(content string) map[string]string { 127 | out := map[string]string{} 128 | for _, line := range strings.Split(content, "\n") { 129 | parts := strings.SplitN(line, "=", 2) 130 | if len(parts) != 2 { 131 | continue 132 | } 133 | key := parts[0] 134 | if len(key) == 0 || key[0] == '#' { 135 | continue 136 | } 137 | // Overwrite previous key. 138 | value := parts[1] 139 | if len(value) > 2 && value[0] == '"' && value[len(value)-1] == '"' { 140 | // Not exactly 100% right but #closeenough. See for more details 141 | // https://www.freedesktop.org/software/systemd/man/os-release.html 142 | var err error 143 | value, err = strconv.Unquote(value) 144 | if err != nil { 145 | continue 146 | } 147 | } 148 | out[key] = value 149 | } 150 | return out 151 | } 152 | 153 | // splitNull returns the null-terminated strings in the data 154 | func splitNull(data []byte) []string { 155 | ss := strings.Split(string(data), "\x00") 156 | // The last string is typically null-terminated, so remove empty string 157 | // from end of array. 158 | if len(ss) > 0 && len(ss[len(ss)-1]) == 0 { 159 | ss = ss[:len(ss)-1] 160 | } 161 | return ss 162 | } 163 | 164 | func makeCPUInfoLinux() map[string]string { 165 | mu.Lock() 166 | defer mu.Unlock() 167 | if cpuInfo == nil { 168 | cpuInfo = map[string]string{} 169 | if bytes, err := readFile("/proc/cpuinfo"); err == nil { 170 | cpuInfo = splitSemiColon(string(bytes)) 171 | } 172 | } 173 | return cpuInfo 174 | } 175 | 176 | func makeOSReleaseLinux() map[string]string { 177 | mu.Lock() 178 | defer mu.Unlock() 179 | if osRelease == nil { 180 | // This file may not exist on older distros. Send a PR if you want to have 181 | // a specific fallback. 182 | osRelease = map[string]string{} 183 | if bytes, err := readFile("/etc/os-release"); err == nil { 184 | osRelease = splitStrict(string(bytes)) 185 | } 186 | } 187 | return osRelease 188 | } 189 | -------------------------------------------------------------------------------- /distro/distro_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package distro 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /distro/distro_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build arm64 6 | // +build arm64 7 | 8 | package distro 9 | 10 | const isArm = true 11 | -------------------------------------------------------------------------------- /distro/distro_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package distro 6 | 7 | const isLinux = true 8 | -------------------------------------------------------------------------------- /distro/distro_nonarm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm && !arm64 6 | // +build !arm,!arm64 7 | 8 | package distro 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /distro/distro_nonlinux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !linux 6 | // +build !linux 7 | 8 | package distro 9 | 10 | const isLinux = false 11 | -------------------------------------------------------------------------------- /distro/distro_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package distro 6 | 7 | import ( 8 | "errors" 9 | "reflect" 10 | "testing" 11 | 12 | "periph.io/x/host/v3/fs" 13 | ) 14 | 15 | func TestSplitSemiColon(t *testing.T) { 16 | data := `/proc/cpuinfo 17 | Processor : AArch64 Processor rev 4 (aarch64) 18 | processor : 0 19 | processor : 1 20 | Features : fp asimd aes pmull sha1 sha2 crc32 21 | CPU architecture: AArch64 22 | CPU part : 0xd03 23 | 24 | Hardware : sun50iw1p1 25 | # foo : bar 26 | ` 27 | expected := map[string]string{ 28 | "CPU architecture": "AArch64", 29 | "CPU part": "0xd03", 30 | "Features": "fp asimd aes pmull sha1 sha2 crc32", 31 | "Hardware": "sun50iw1p1", 32 | "Processor": "AArch64 Processor rev 4 (aarch64)", 33 | "processor": "0", 34 | } 35 | if actual := splitSemiColon(data); !reflect.DeepEqual(actual, expected) { 36 | t.Fatalf("%# v != %# v", expected, actual) 37 | } 38 | } 39 | 40 | func TestSplitStrict(t *testing.T) { 41 | data := `PRETTY_NAME="Raspbian GNU/Linux 8 (jessie)" 42 | VERSION_ID="8" 43 | VERSION="8 (jessie)" 44 | ID_LIKE=debian 45 | HOME_URL="http://www.raspbian.org/" 46 | # foo : bar 47 | # foo = bar 48 | FOO="aa"" 49 | ` 50 | expected := map[string]string{ 51 | "HOME_URL": "http://www.raspbian.org/", 52 | "ID_LIKE": "debian", 53 | "PRETTY_NAME": "Raspbian GNU/Linux 8 (jessie)", 54 | "VERSION": "8 (jessie)", 55 | "VERSION_ID": "8", 56 | } 57 | if actual := splitStrict(data); !reflect.DeepEqual(actual, expected) { 58 | t.Fatalf("%# v != %# v", expected, actual) 59 | } 60 | } 61 | 62 | func TestSplitNull(t *testing.T) { 63 | data := []byte("line 1\x00line 2\x00line 3\x00") 64 | expected := []string{"line 1", "line 2", "line 3"} 65 | if actual := splitNull(data); !reflect.DeepEqual(actual, expected) { 66 | t.Fatalf("%# v != %# v", expected, actual) 67 | } 68 | 69 | data = []byte("") 70 | expected = []string{} 71 | if actual := splitNull(data); !reflect.DeepEqual(actual, expected) { 72 | t.Fatalf("%# v != %# v", expected, actual) 73 | } 74 | } 75 | 76 | func TestIsArmbian(t *testing.T) { 77 | // At least ensure it doesn't crash. 78 | IsArmbian() 79 | } 80 | 81 | func TestIsDebian(t *testing.T) { 82 | // At least ensure it doesn't crash. 83 | IsDebian() 84 | } 85 | 86 | func TestIsRaspbian(t *testing.T) { 87 | // At least ensure it doesn't crash. 88 | IsRaspbian() 89 | } 90 | 91 | func TestIsUbuntu(t *testing.T) { 92 | // At least ensure it doesn't crash. 93 | IsUbuntu() 94 | } 95 | 96 | func TestCPUInfo_fail(t *testing.T) { 97 | defer reset() 98 | if c := CPUInfo(); len(c) != 0 { 99 | t.Fatal(c) 100 | } 101 | } 102 | 103 | func TestCPUInfo(t *testing.T) { 104 | defer reset() 105 | readFile = func(filename string) ([]byte, error) { 106 | if filename != "/proc/cpuinfo" { 107 | t.Fatal(filename) 108 | } 109 | return []byte("Processor : AArch64\n"), nil 110 | } 111 | CPUInfo() 112 | expected := map[string]string{"Processor": "AArch64"} 113 | if c := makeCPUInfoLinux(); !reflect.DeepEqual(c, expected) { 114 | t.Fatal(c) 115 | } 116 | } 117 | 118 | func TestOSRelease_fail(t *testing.T) { 119 | defer reset() 120 | if c := OSRelease(); len(c) != 0 { 121 | t.Fatal(c) 122 | } 123 | } 124 | 125 | func TestOSRelease(t *testing.T) { 126 | defer reset() 127 | readFile = func(filename string) ([]byte, error) { 128 | if filename != "/etc/os-release" { 129 | t.Fatal(filename) 130 | } 131 | return []byte("VERSION_ID=8\n"), nil 132 | } 133 | OSRelease() 134 | expected := map[string]string{"VERSION_ID": "8"} 135 | if c := makeOSReleaseLinux(); !reflect.DeepEqual(c, expected) { 136 | t.Fatal(c) 137 | } 138 | } 139 | 140 | // 141 | 142 | func init() { 143 | fs.Inhibit() 144 | reset() 145 | } 146 | 147 | func reset() { 148 | cpuInfo = nil 149 | dtCompatible = nil 150 | dtModel = "" 151 | osRelease = nil 152 | readFile = func(filename string) ([]byte, error) { 153 | return nil, errors.New("no file can be opened in unit tests") 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package host defines the host itself. 6 | // 7 | // The host is the machine where this code is running. 8 | // 9 | // Subpackages contain the drivers that are loaded automatically. 10 | package host 11 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package host_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/host/v3" 12 | ) 13 | 14 | func ExampleInit() { 15 | // Make sure periph is initialized. 16 | state, err := host.Init() 17 | if err != nil { 18 | log.Fatalf("failed to initialize periph: %v", err) 19 | } 20 | 21 | // Prints the loaded driver. 22 | fmt.Printf("Using drivers:\n") 23 | for _, driver := range state.Loaded { 24 | fmt.Printf("- %s\n", driver) 25 | } 26 | 27 | // Prints the driver that were skipped as irrelevant on the platform. 28 | fmt.Printf("Drivers skipped:\n") 29 | for _, failure := range state.Skipped { 30 | fmt.Printf("- %s: %s\n", failure.D, failure.Err) 31 | } 32 | 33 | // Having drivers failing to load may not require process termination. It 34 | // is possible to continue to run in partial failure mode. 35 | fmt.Printf("Drivers failed to load:\n") 36 | for _, failure := range state.Failed { 37 | fmt.Printf("- %s: %v\n", failure.D, failure.Err) 38 | } 39 | 40 | // Use pins, buses, devices, etc. 41 | } 42 | -------------------------------------------------------------------------------- /fs/fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package fs provides access to the file system on the host. 6 | // 7 | // It exposes ioctl syscall and epoll in an OS agnostic way and permits 8 | // completely disabling file access to lock down unit tests. 9 | package fs 10 | 11 | import ( 12 | "errors" 13 | "os" 14 | "sync" 15 | ) 16 | 17 | // Ioctler is a file handle that supports ioctl calls. 18 | type Ioctler interface { 19 | // Ioctl sends a linux ioctl on the file handle. 20 | // 21 | // op is effectively an uint32. op is expected to be encoded in the format on 22 | // x64. ARM happens to share the same format. 23 | Ioctl(op uint, data uintptr) error 24 | } 25 | 26 | // Open opens a file. 27 | // 28 | // Returns an error if Inhibit() was called. 29 | func Open(path string, flag int) (*File, error) { 30 | mu.Lock() 31 | if inhibited { 32 | mu.Unlock() 33 | return nil, errors.New("file I/O is inhibited") 34 | } 35 | used = true 36 | mu.Unlock() 37 | 38 | f, err := os.OpenFile(path, flag, 0600) 39 | if err != nil { 40 | return nil, err 41 | } 42 | return &File{f}, nil 43 | } 44 | 45 | // Inhibit inhibits any future file I/O. It panics if any file was opened up to 46 | // now. 47 | // 48 | // It should only be called in unit tests. 49 | func Inhibit() { 50 | mu.Lock() 51 | inhibited = true 52 | if used { 53 | panic("calling Inhibit() while files were already opened") 54 | } 55 | mu.Unlock() 56 | } 57 | 58 | // File is a superset of os.File. 59 | type File struct { 60 | *os.File 61 | } 62 | 63 | // Ioctl sends an ioctl to the file handle. 64 | func (f *File) Ioctl(op uint, data uintptr) error { 65 | return ioctl(f.Fd(), op, data) 66 | } 67 | 68 | // Event is a file system event. 69 | type Event struct { 70 | event 71 | } 72 | 73 | // MakeEvent initializes an epoll *edge* triggered event on linux. 74 | // 75 | // An edge triggered event is basically an "auto-reset" event, where waiting on 76 | // the edge resets it. A level triggered event requires manual resetting; this 77 | // could be done via a Read() call but there's no need to require the user to 78 | // call Read(). This is particularly useless in the case of gpio.RisingEdge and 79 | // gpio.FallingEdge. 80 | // 81 | // As per the official doc, edge triggers is still remembered even when no 82 | // epoll_wait() call is running, so no edge is missed. Two edges will be 83 | // coallesced into one if the user mode process can't keep up. There's no 84 | // accumulation of edges. 85 | func (e *Event) MakeEvent(fd uintptr) error { 86 | return e.event.makeEvent(fd) 87 | } 88 | 89 | // Wait waits for an event or the specified amount of time. 90 | func (e *Event) Wait(timeoutms int) (int, error) { 91 | return e.event.wait(timeoutms) 92 | } 93 | 94 | // 95 | 96 | var ( 97 | mu sync.Mutex 98 | inhibited bool 99 | used bool 100 | ) 101 | -------------------------------------------------------------------------------- /fs/fs_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package fs 6 | 7 | import ( 8 | "strconv" 9 | "strings" 10 | "syscall" 11 | ) 12 | 13 | const isLinux = true 14 | 15 | // syscall.EpollCtl() commands. 16 | // 17 | // These are defined here so we don't need to import golang.org/x/sys/unix. 18 | // 19 | // http://man7.org/linux/man-pages/man2/epoll_ctl.2.html 20 | const ( 21 | epollCTLAdd = 1 // EPOLL_CTL_ADD 22 | epollCTLDel = 2 // EPOLL_CTL_DEL 23 | epollCTLMod = 3 // EPOLL_CTL_MOD 24 | ) 25 | 26 | // Bitmask for field syscall.EpollEvent.Events. 27 | // 28 | // These are defined here so we don't need to import golang.org/x/sys/unix. 29 | // 30 | // http://man7.org/linux/man-pages/man2/epoll_ctl.2.html 31 | type epollEvent uint32 32 | 33 | const ( 34 | epollIN epollEvent = 0x1 // EPOLLIN: available for read 35 | epollOUT epollEvent = 0x4 // EPOLLOUT: available for write 36 | epollPRI epollEvent = 0x2 // EPOLLPRI: exceptional urgent condition 37 | epollERR epollEvent = 0x8 // EPOLLERR: error 38 | epollHUP epollEvent = 0x10 // EPOLLHUP: hangup 39 | epollET epollEvent = 0x80000000 // EPOLLET: Edge Triggered behavior 40 | epollONESHOT epollEvent = 0x40000000 // EPOLLONESHOT: One shot 41 | epollWAKEUP epollEvent = 0x20000000 // EPOLLWAKEUP: disable system sleep; kernel >=3.5 42 | epollEXCLUSIVE epollEvent = 0x10000000 // EPOLLEXCLUSIVE: only wake one; kernel >=4.5 43 | ) 44 | 45 | var bitmaskString = [...]struct { 46 | e epollEvent 47 | s string 48 | }{ 49 | {epollIN, "IN"}, 50 | {epollOUT, "OUT"}, 51 | {epollPRI, "PRI"}, 52 | {epollERR, "ERR"}, 53 | {epollHUP, "HUP"}, 54 | {epollET, "ET"}, 55 | {epollONESHOT, "ONESHOT"}, 56 | {epollWAKEUP, "WAKEUP"}, 57 | {epollEXCLUSIVE, "EXCLUSIVE"}, 58 | } 59 | 60 | // String is useful for debugging. 61 | func (e epollEvent) String() string { 62 | var out []string 63 | for _, b := range bitmaskString { 64 | if e&b.e != 0 { 65 | out = append(out, b.s) 66 | e &^= b.e 67 | } 68 | } 69 | if e != 0 { 70 | out = append(out, "0x"+strconv.FormatUint(uint64(e), 16)) 71 | } 72 | if len(out) == 0 { 73 | out = []string{"0"} 74 | } 75 | return strings.Join(out, "|") 76 | } 77 | 78 | func ioctl(f uintptr, op uint, arg uintptr) error { 79 | if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f, uintptr(op), arg); errno != 0 { 80 | return syscall.Errno(errno) 81 | } 82 | return nil 83 | } 84 | 85 | type event struct { 86 | event [1]syscall.EpollEvent 87 | epollFd int 88 | fd int 89 | } 90 | 91 | // makeEvent creates an epoll *edge* triggered event. 92 | // 93 | // References: 94 | // behavior and flags: http://man7.org/linux/man-pages/man7/epoll.7.html 95 | // syscall.EpollCreate: http://man7.org/linux/man-pages/man2/epoll_create.2.html 96 | // syscall.EpollCtl: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html 97 | func (e *event) makeEvent(fd uintptr) error { 98 | epollFd, err := syscall.EpollCreate1(syscall.EPOLL_CLOEXEC) 99 | switch { 100 | case err == nil: 101 | break 102 | case err.Error() == "function not implemented": 103 | // Fall back to epoll_create. 104 | if epollFd, err = syscall.EpollCreate(1); err != nil { 105 | return err 106 | } 107 | default: 108 | return err 109 | } 110 | e.epollFd = epollFd 111 | e.fd = int(fd) 112 | // EPOLLWAKEUP could be used to force the system to not go do sleep while 113 | // waiting for an edge. This is generally a bad idea, as we'd instead have 114 | // the system to *wake up* when an edge is triggered. Achieving this is 115 | // outside the scope of this interface. 116 | e.event[0].Events = uint32(epollPRI | epollET) 117 | e.event[0].Fd = int32(e.fd) 118 | return syscall.EpollCtl(e.epollFd, epollCTLAdd, e.fd, &e.event[0]) 119 | } 120 | 121 | func (e *event) wait(timeoutms int) (int, error) { 122 | // http://man7.org/linux/man-pages/man2/epoll_wait.2.html 123 | return syscall.EpollWait(e.epollFd, e.event[:], timeoutms) 124 | } 125 | -------------------------------------------------------------------------------- /fs/fs_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !linux 6 | // +build !linux 7 | 8 | package fs 9 | 10 | import "errors" 11 | 12 | const isLinux = false 13 | 14 | func ioctl(f uintptr, op uint, arg uintptr) error { 15 | return errors.New("fs: ioctl not supported on non-linux") 16 | } 17 | 18 | type event struct{} 19 | 20 | func (e *event) makeEvent(f uintptr) error { 21 | return errors.New("fs: unreachable code") 22 | } 23 | 24 | func (e *event) wait(timeoutms int) (int, error) { 25 | return 0, errors.New("fs: unreachable code") 26 | } 27 | -------------------------------------------------------------------------------- /fs/ioctl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package fs 6 | 7 | // These constants, variables and functions are ported from the Linux userland 8 | // API header ioctl.h (commonly packaged at /usr/include/linux/ioctl.h which 9 | // includes /usr/include/asm-generic/ioctl.h). 10 | 11 | const ( 12 | iocNrbits uint = 8 13 | iocTypebits uint = 8 14 | 15 | iocNrshift uint = 0 16 | 17 | iocTypeshift = iocNrshift + iocNrbits 18 | iocSizeshift = iocTypeshift + iocTypebits 19 | iocDirshift = iocSizeshift + iocSizebits 20 | ) 21 | 22 | func ioc(dir, typ, nr, size uint) uint { 23 | return (dir << iocDirshift) | 24 | (typ << iocTypeshift) | 25 | (nr << iocNrshift) | 26 | (size << iocSizeshift) 27 | } 28 | 29 | // IO defines an ioctl with no parameters. It corresponds to _IO in the Linux 30 | // userland API. 31 | func IO(typ, nr uint) uint { 32 | return ioc(iocNone, typ, nr, 0) 33 | } 34 | 35 | // IOR defines an ioctl with read (userland perspective) parameters. It 36 | // corresponds to _IOR in the Linux userland API. 37 | func IOR(typ, nr, size uint) uint { 38 | return ioc(iocRead, typ, nr, size) 39 | } 40 | 41 | // IOW defines an ioctl with write (userland perspective) parameters. It 42 | // corresponds to _IOW in the Linux userland API. 43 | func IOW(typ, nr, size uint) uint { 44 | return ioc(iocWrite, typ, nr, size) 45 | } 46 | 47 | // IOWR defines an ioctl with both read and write parameters. It corresponds to 48 | // _IOWR in the Linux userland API. 49 | func IOWR(typ, nr, size uint) uint { 50 | return ioc(iocRead|iocWrite, typ, nr, size) 51 | } 52 | -------------------------------------------------------------------------------- /fs/ioctl_mips_like.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build mips || mipsle 6 | // +build mips mipsle 7 | 8 | package fs 9 | 10 | const ( 11 | iocNone uint = 1 12 | iocRead uint = 2 13 | iocWrite uint = 4 14 | 15 | iocSizebits uint = 13 16 | iocDirbits uint = 3 17 | ) 18 | -------------------------------------------------------------------------------- /fs/ioctl_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !mips && !mipsle 6 | // +build !mips,!mipsle 7 | 8 | package fs 9 | 10 | const ( 11 | iocNone uint = 0 12 | iocWrite uint = 1 13 | iocRead uint = 2 14 | 15 | iocSizebits uint = 14 16 | iocDirbits uint = 2 17 | ) 18 | -------------------------------------------------------------------------------- /ftdi/debian/98-ft232h.rules: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Periph Authors. All rights reserved. 2 | # Use of this source code is governed under the Apache License, Version 2.0 3 | # that can be found in the LICENSE file. 4 | 5 | # udev rule file to make FTDI devices accessible usable as non-root and disable 6 | # the ftdi_sio driver. 7 | # 8 | # See https://periph.io/device/ftdi/ for more information. 9 | 10 | # Make FTDI device usable from users in group plugdev. 11 | SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", MODE="0664", GROUP="plugdev" 12 | 13 | # Disallow ftdi_sio from loading. 14 | SUBSYSTEM=="usb", DRIVER=="ftdi_sio", ATTRS{idVendor}=="0403", RUN+="/bin/sh -c 'echo $kernel > /sys/bus/usb/drivers/ftdi_sio/unbind'" 15 | -------------------------------------------------------------------------------- /ftdi/debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build periph_host_ftdi_debug 6 | // +build periph_host_ftdi_debug 7 | 8 | package ftdi 9 | 10 | import ( 11 | "log" 12 | 13 | "periph.io/x/d2xx" 14 | "periph.io/x/d2xx/d2xxtest" 15 | ) 16 | 17 | // logf is enabled when the build tag host_ftdi_debug is specified. 18 | func logf(fmt string, v ...interface{}) { 19 | log.Printf(fmt, v...) 20 | } 21 | 22 | func (d *driver) resetLog() { 23 | d.d2xxOpen = func(i int) (d2xx.Handle, d2xx.Err) { 24 | h, e := d2xx.Open(i) 25 | if e != 0 { 26 | return h, e 27 | } 28 | return &d2xxtest.Log{H: h, Printf: logf}, e 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ftdi/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package ftdi implements support for popular FTDI devices. 6 | // 7 | // The supported devices (FT232h/FT232r) implement support for various 8 | // protocols like the GPIO, I²C, SPI, UART, JTAG. 9 | // 10 | // Use build tag periph_host_ftdi_debug to enable verbose debugging. 11 | // 12 | // # More details 13 | // 14 | // See https://periph.io/device/ftdi/ for more details, and how to configure 15 | // the host to be able to use this driver. 16 | // 17 | // # Datasheets 18 | // 19 | // http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232R.pdf 20 | // 21 | // http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232H.pdf 22 | package ftdi 23 | -------------------------------------------------------------------------------- /ftdi/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package ftdi 6 | 7 | import ( 8 | "strconv" 9 | "sync" 10 | 11 | "periph.io/x/conn/v3/driver/driverreg" 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpioreg" 14 | "periph.io/x/conn/v3/i2c" 15 | "periph.io/x/conn/v3/i2c/i2creg" 16 | "periph.io/x/conn/v3/pin" 17 | "periph.io/x/conn/v3/pin/pinreg" 18 | "periph.io/x/conn/v3/spi/spireg" 19 | "periph.io/x/d2xx" 20 | ) 21 | 22 | // All enumerates all the connected FTDI devices. 23 | func All() []Dev { 24 | drv.mu.Lock() 25 | defer drv.mu.Unlock() 26 | out := make([]Dev, len(drv.all)) 27 | copy(out, drv.all) 28 | return out 29 | } 30 | 31 | // rescan rescans the USB bus for new or disconnected devices. 32 | func rescan() error { 33 | drv.mu.Lock() 34 | defer drv.mu.Unlock() 35 | e := d2xx.Rescan() 36 | // TODO(maruel): Reenumerate the devices. 37 | if e != 0 { 38 | return toErr("Rescan", e) 39 | } 40 | return nil 41 | } 42 | 43 | // 44 | 45 | // open opens a FTDI device. 46 | // 47 | // Must be called with mu held. 48 | func open(opener func(i int) (d2xx.Handle, d2xx.Err), i int) (Dev, error) { 49 | h, err := openHandle(opener, i) 50 | if err != nil { 51 | return nil, err 52 | } 53 | if err := h.Init(); err != nil { 54 | // setupCommon() takes the device in its previous state. It could be in an 55 | // unexpected state, so try resetting it first. 56 | if err := h.Reset(); err != nil { 57 | _ = h.Close() 58 | return nil, err 59 | } 60 | if err := h.Init(); err != nil { 61 | _ = h.Close() 62 | return nil, err 63 | } 64 | // The second attempt worked. 65 | } 66 | // Makes a copy of the handle. 67 | g := generic{index: i, h: h, name: h.t.String()} 68 | if i > 0 { 69 | // When more than one device is present, add "(index)" suffix. 70 | // TODO(maruel): Using the serial number would be nicer than a number. 71 | g.name += "(" + strconv.Itoa(i) + ")" 72 | } 73 | // Makes a copy of the generic instance. 74 | switch g.h.t { 75 | case DevTypeFT232H: 76 | f, err := newFT232H(g) 77 | if err != nil { 78 | _ = h.Close() 79 | return nil, err 80 | } 81 | return f, nil 82 | case DevTypeFT2232H: 83 | f, err := newFT232H(g) 84 | if err != nil { 85 | _ = h.Close() 86 | return nil, err 87 | } 88 | return f, nil 89 | case DevTypeFT232R: 90 | f, err := newFT232R(g) 91 | if err != nil { 92 | _ = h.Close() 93 | return nil, err 94 | } 95 | return f, nil 96 | default: 97 | return &g, nil 98 | } 99 | } 100 | 101 | // registerDev registers the header and supported buses and ports in the 102 | // relevant registries. 103 | func registerDev(d Dev, multi bool) error { 104 | name := d.String() 105 | hdr := d.Header() 106 | 107 | // Register the GPIOs. 108 | for _, p := range hdr { 109 | if err := gpioreg.Register(p); err != nil { 110 | return err 111 | } 112 | } 113 | if !multi { 114 | // Register shorthands. 115 | // The "." used here vs the "_" used in pinreg is unfortunate. Investigate 116 | // a better way. 117 | prefix := len(name) + 1 118 | for _, p := range hdr { 119 | n := p.Name() 120 | if err := gpioreg.RegisterAlias(n[prefix:], n); err != nil { 121 | return err 122 | } 123 | } 124 | } 125 | 126 | // Register the header. 127 | raw := make([][]pin.Pin, len(hdr)) 128 | for i := range hdr { 129 | raw[i] = []pin.Pin{hdr[i]} 130 | } 131 | if err := pinreg.Register(name, raw); err != nil { 132 | return err 133 | } 134 | switch t := d.(type) { 135 | case *FT232H: 136 | // Register I²C without pull up. 137 | if err := i2creg.Register(name, nil, -1, func() (i2c.BusCloser, error) { return t.I2C(gpio.Float) }); err != nil { 138 | return err 139 | } 140 | if err := spireg.Register(name, nil, -1, t.SPI); err != nil { 141 | return err 142 | } 143 | // TODO(maruel): UART 144 | case *FT232R: 145 | // TODO(maruel): SPI, UART 146 | } 147 | return nil 148 | } 149 | 150 | // driver implements driver.Impl. 151 | type driver struct { 152 | mu sync.Mutex 153 | all []Dev 154 | d2xxOpen func(i int) (d2xx.Handle, d2xx.Err) 155 | numDevices func() (int, error) 156 | } 157 | 158 | func (d *driver) String() string { 159 | return "ftdi" 160 | } 161 | 162 | func (d *driver) Prerequisites() []string { 163 | return nil 164 | } 165 | 166 | func (d *driver) After() []string { 167 | return nil 168 | } 169 | 170 | func (d *driver) Init() (bool, error) { 171 | num, err := d.numDevices() 172 | if err != nil { 173 | return true, err 174 | } 175 | multi := num > 1 176 | for i := 0; i < num; i++ { 177 | // TODO(maruel): Close the device one day. :) 178 | if dev, err1 := open(d.d2xxOpen, i); err1 == nil { 179 | d.all = append(d.all, dev) 180 | if err = registerDev(dev, multi); err != nil { 181 | return true, err 182 | } 183 | } else { 184 | // Create a shallow broken handle, so the user can learn how to fix the 185 | // problem. 186 | // 187 | // TODO(maruel): On macOS with a FT232R, calling two processes in a row 188 | // often results in a broken device on the second process. Figure out why 189 | // and make it more resilient. 190 | err = err1 191 | // The serial number is not available so what can be listed is limited. 192 | // TODO(maruel): Add VID/PID? 193 | name := "broken#" + strconv.Itoa(i) + ": " + err.Error() 194 | d.all = append(d.all, &broken{index: i, err: err, name: name}) 195 | } 196 | } 197 | return true, err 198 | } 199 | 200 | func (d *driver) reset() { 201 | d.mu.Lock() 202 | defer d.mu.Unlock() 203 | d.all = nil 204 | // open is mocked in tests. You can also wrap d2xx.Open to return a wrapped 205 | // d2xxtest.Log. 206 | d.d2xxOpen = d2xx.Open 207 | // numDevices is mocked in tests. 208 | d.numDevices = numDevices 209 | } 210 | 211 | func init() { 212 | if d2xx.Available { 213 | drv.reset() 214 | drv.resetLog() 215 | driverreg.MustRegister(&drv) 216 | } 217 | } 218 | 219 | var drv driver 220 | -------------------------------------------------------------------------------- /ftdi/driver_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package ftdi 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/d2xx" 11 | "periph.io/x/d2xx/d2xxtest" 12 | ) 13 | 14 | func TestDriver(t *testing.T) { 15 | defer reset(t) 16 | drv.numDevices = func() (int, error) { 17 | return 1, nil 18 | } 19 | drv.d2xxOpen = func(i int) (d2xx.Handle, d2xx.Err) { 20 | if i != 0 { 21 | t.Fatalf("unexpected index %d", i) 22 | } 23 | d := &d2xxtest.Fake{ 24 | DevType: uint32(DevTypeFT232R), 25 | Vid: 0x0403, 26 | Pid: 0x6014, 27 | Data: [][]byte{{}, {0}}, 28 | } 29 | return d, 0 30 | } 31 | if b, err := drv.Init(); !b || err != nil { 32 | t.Fatalf("Init() = %t, %v", b, err) 33 | } 34 | } 35 | 36 | func reset(t *testing.T) { 37 | drv.reset() 38 | } 39 | 40 | func init() { 41 | reset(nil) 42 | } 43 | -------------------------------------------------------------------------------- /ftdi/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package ftdi_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/host/v3" 12 | "periph.io/x/host/v3/ftdi" 13 | ) 14 | 15 | func Example() { 16 | if _, err := host.Init(); err != nil { 17 | log.Fatal(err) 18 | } 19 | for _, d := range ftdi.All() { 20 | fmt.Printf("%s\n", d) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ftdi/ftdi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package ftdi 6 | -------------------------------------------------------------------------------- /ftdi/ftdismoketest/README.md: -------------------------------------------------------------------------------- 1 | # FTDI smoke test 2 | 3 | Verifies that a FT232H/FT232R works as expected. 4 | -------------------------------------------------------------------------------- /ftdi/no_debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !periph_host_ftdi_debug 6 | // +build !periph_host_ftdi_debug 7 | 8 | package ftdi 9 | 10 | // logf is disabled when the build tag host_ftdi_debug is not specified. 11 | func logf(fmt string, v ...interface{}) { 12 | } 13 | 14 | func (d *driver) resetLog() { 15 | } 16 | -------------------------------------------------------------------------------- /ftdi/pin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Emulate independent GPIOs. 6 | 7 | package ftdi 8 | 9 | import ( 10 | "errors" 11 | "time" 12 | 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/conn/v3/physic" 15 | ) 16 | 17 | // invalidPin is a non-working (not implemented) pin on a FTDI device. 18 | // 19 | // invalidPin implements gpio.PinIO. 20 | type invalidPin struct { 21 | n string 22 | num int 23 | } 24 | 25 | // String implements pin.Pin. 26 | func (p *invalidPin) String() string { 27 | return p.n 28 | } 29 | 30 | // Name implements pin.Pin. 31 | func (p *invalidPin) Name() string { 32 | return p.n 33 | } 34 | 35 | // Number implements pin.Pin. 36 | func (p *invalidPin) Number() int { 37 | return p.num 38 | } 39 | 40 | // Function implements pin.Pin. 41 | func (p *invalidPin) Function() string { 42 | return "N/A" 43 | } 44 | 45 | // Halt implements gpio.PinIO. 46 | func (p *invalidPin) Halt() error { 47 | return nil 48 | } 49 | 50 | // In implements gpio.PinIn. 51 | func (p *invalidPin) In(pull gpio.Pull, e gpio.Edge) error { 52 | return errors.New("d2xx: to be implemented") 53 | } 54 | 55 | // Read implements gpio.PinIn. 56 | func (p *invalidPin) Read() gpio.Level { 57 | return gpio.Low 58 | } 59 | 60 | // WaitForEdge implements gpio.PinIn. 61 | func (p *invalidPin) WaitForEdge(t time.Duration) bool { 62 | return false 63 | } 64 | 65 | // Pull implements gpio.PinIn. 66 | func (p *invalidPin) Pull() gpio.Pull { 67 | return gpio.PullNoChange 68 | } 69 | 70 | // DefaultPull implements gpio.PinIn. 71 | func (p *invalidPin) DefaultPull() gpio.Pull { 72 | return gpio.PullNoChange 73 | } 74 | 75 | // Out implements gpio.PinOut. 76 | func (p *invalidPin) Out(l gpio.Level) error { 77 | return errors.New("d2xx: to be implemented") 78 | } 79 | 80 | // PWM implements gpio.PinOut. 81 | func (p *invalidPin) PWM(d gpio.Duty, f physic.Frequency) error { 82 | return errors.New("d2xx: to be implemented") 83 | } 84 | 85 | var _ gpio.PinIO = &invalidPin{} 86 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | module periph.io/x/host/v3 6 | 7 | go 1.22.6 8 | 9 | require ( 10 | periph.io/x/conn/v3 v3.7.2 11 | periph.io/x/d2xx v0.1.1 12 | ) 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= 2 | github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= 3 | periph.io/x/conn/v3 v3.7.2 h1:qt9dE6XGP5ljbFnCKRJ9OOCoiOyBGlw7JZgoi72zZ1s= 4 | periph.io/x/conn/v3 v3.7.2/go.mod h1:Ao0b4sFRo4QOx6c1tROJU1fLJN1hUIYggjOrkIVnpGg= 5 | periph.io/x/d2xx v0.1.1 h1:LHp+u+qAWLB5THrTT/AzyjdvfUhllvDF5wBJP7uvn+U= 6 | periph.io/x/d2xx v0.1.1/go.mod h1:rLM321G11Fc14Pp088khBkmXb70Pxx/kCPaIK7uRUBc= 7 | -------------------------------------------------------------------------------- /gpioioctl/README.md: -------------------------------------------------------------------------------- 1 | # GPIO IOCTL 2 | 3 | This directory contains an implementation for Linux GPIO manipulation 4 | using the ioctl v2 interface. 5 | 6 | Basic test is provided, but a much more complete smoke test is provided 7 | in periph.io/x/cmd/periph-smoketest/gpiosmoketest 8 | -------------------------------------------------------------------------------- /gpioioctl/basic_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Basic tests. More complete test is contained in the 6 | // periph.io/x/cmd/periph-smoketest/gpiosmoketest 7 | // folder. 8 | 9 | package gpioioctl 10 | 11 | import ( 12 | "testing" 13 | 14 | "periph.io/x/conn/v3/gpio/gpioreg" 15 | ) 16 | 17 | var testLine *GPIOLine 18 | 19 | func init() { 20 | if len(Chips) == 0 { 21 | makeDummyChip() 22 | } 23 | } 24 | 25 | func TestChips(t *testing.T) { 26 | chip := Chips[0] 27 | t.Log(chip.String()) 28 | if len(chip.Name()) == 0 { 29 | t.Error("chip.Name() is 0 length") 30 | } 31 | if len(chip.Path()) == 0 { 32 | t.Error("chip path is 0 length") 33 | } 34 | if len(chip.Label()) == 0 { 35 | t.Error("chip label is 0 length!") 36 | } 37 | if len(chip.Lines()) != chip.LineCount() { 38 | t.Errorf("Incorrect line count. Found: %d for LineCount, Returned Lines length=%d", chip.LineCount(), len(chip.Lines())) 39 | } 40 | for _, line := range chip.Lines() { 41 | if len(line.Consumer()) == 0 && len(line.Name()) > 0 { 42 | testLine = line 43 | break 44 | } 45 | } 46 | if testLine == nil { 47 | t.Error("Error finding unused line for testing!") 48 | } 49 | for _, c := range Chips { 50 | s := c.String() 51 | if len(s) == 0 { 52 | t.Error("Error calling chip.String(). No output returned!") 53 | } else { 54 | t.Log(s) 55 | } 56 | 57 | } 58 | 59 | } 60 | 61 | func TestGPIORegistryByName(t *testing.T) { 62 | if testLine == nil { 63 | return 64 | } 65 | outLine := gpioreg.ByName(testLine.Name()) 66 | if outLine == nil { 67 | t.Fatalf("Error retrieving GPIO Line %s", testLine.Name()) 68 | } 69 | if outLine.Name() != testLine.Name() { 70 | t.Errorf("Error checking name. Expected %s, received %s", testLine.Name(), outLine.Name()) 71 | } 72 | 73 | if outLine.Number() < 0 || outLine.Number() >= len(Chips[0].Lines()) { 74 | t.Errorf("Invalid chip number %d received for %s", outLine.Number(), testLine.Name()) 75 | } 76 | } 77 | 78 | func TestNumber(t *testing.T) { 79 | chip := Chips[0] 80 | if testLine == nil { 81 | return 82 | } 83 | l := chip.ByName(testLine.Name()) 84 | if l == nil { 85 | t.Fatalf("Error retrieving GPIO Line %s", testLine.Name()) 86 | } 87 | if l.Number() < 0 || l.Number() >= chip.LineCount() { 88 | t.Errorf("line.Number() returned value (%d) out of range", l.Number()) 89 | } 90 | l2 := chip.ByNumber(l.Number()) 91 | if l2 == nil { 92 | t.Errorf("retrieve Line from chip by number %d failed.", l.Number()) 93 | } 94 | 95 | } 96 | 97 | func TestString(t *testing.T) { 98 | if testLine == nil { 99 | return 100 | } 101 | line := gpioreg.ByName(testLine.Name()) 102 | if line == nil { 103 | t.Fatalf("Error retrieving GPIO Line %s", testLine.Name()) 104 | } 105 | s := line.String() 106 | if len(s) == 0 { 107 | t.Errorf("GPIOLine.String() failed.") 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /gpioioctl/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | // 5 | // Package gpioioctl provides access to Linux GPIO lines using the ioctl interface. 6 | // 7 | // https://docs.kernel.org/userspace-api/gpio/index.html 8 | // 9 | // GPIO Pins can be accessed via periph.io/x/conn/v3/gpio/gpioreg, 10 | // or using the Chips collection to access the specific GPIO chip 11 | // and using it's ByName()/ByNumber methods. 12 | // 13 | // GPIOChip provides a LineSet feature that allows you to atomically 14 | // read/write to multiple GPIO pins as a single operation. 15 | package gpioioctl 16 | -------------------------------------------------------------------------------- /gpioioctl/dummy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | // 5 | // Create a dummy chip for testing an non-Linux os. 6 | 7 | package gpioioctl 8 | 9 | import ( 10 | "log" 11 | 12 | "periph.io/x/conn/v3/gpio" 13 | "periph.io/x/conn/v3/gpio/gpioreg" 14 | ) 15 | 16 | func makeDummyChip() { 17 | /* 18 | During pipeline builds, GPIOChips may not be available, or 19 | it may build on another OS. In that case, mock in enough 20 | for a test to pass. 21 | */ 22 | 23 | line := GPIOLine{ 24 | number: 0, 25 | name: "DummyGPIOLine", 26 | consumer: "", 27 | edge: gpio.NoEdge, 28 | pull: gpio.PullNoChange, 29 | direction: LineDirNotSet, 30 | } 31 | 32 | chip := GPIOChip{name: "DummyGPIOChip", 33 | path: "/dev/gpiochipdummy", 34 | label: "Dummy GPIOChip for Testing Purposes", 35 | lineCount: 1, 36 | lines: []*GPIOLine{&line}, 37 | } 38 | Chips = append(Chips, &chip) 39 | if err := gpioreg.Register(&line); err != nil { 40 | nameStr := chip.Name() 41 | lineStr := line.String() 42 | log.Println("chip", nameStr, " gpioreg.Register(line) ", lineStr, " returned ", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /gpioioctl/example_test.go: -------------------------------------------------------------------------------- 1 | package gpioioctl_test 2 | 3 | // Copyright 2024 The Periph Authors. All rights reserved. 4 | // Use of this source code is governed under the Apache License, Version 2.0 5 | // that can be found in the LICENSE file. 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "time" 11 | 12 | "periph.io/x/conn/v3/driver/driverreg" 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/conn/v3/gpio/gpioreg" 15 | "periph.io/x/host/v3" 16 | "periph.io/x/host/v3/gpioioctl" 17 | ) 18 | 19 | func Example() { 20 | _, _ = host.Init() 21 | _, _ = driverreg.Init() 22 | 23 | fmt.Println("GPIO Test Program") 24 | chip := gpioioctl.Chips[0] 25 | defer chip.Close() 26 | fmt.Println(chip.String()) 27 | // Test by flashing an LED. 28 | led := gpioreg.ByName("GPIO5") 29 | fmt.Println("Flashing LED ", led.Name()) 30 | for i := range 20 { 31 | _ = led.Out((i % 2) == 0) 32 | time.Sleep(500 * time.Millisecond) 33 | } 34 | _ = led.Out(true) 35 | 36 | testRotary(chip, "GPIO20", "GPIO21", "GPIO19") 37 | } 38 | 39 | // Test the LineSet functionality by using it to read a Rotary Encoder w/ Button. 40 | func testRotary(chip *gpioioctl.GPIOChip, stateLine, dataLine, buttonLine string) { 41 | config := gpioioctl.LineSetConfig{DefaultDirection: gpioioctl.LineInput, DefaultEdge: gpio.RisingEdge, DefaultPull: gpio.PullUp} 42 | config.Lines = []string{stateLine, dataLine, buttonLine} 43 | // The Data Pin of the Rotary Encoder should NOT have an edge. 44 | _ = config.AddOverrides(gpioioctl.LineInput, gpio.NoEdge, gpio.PullUp, dataLine) 45 | ls, err := chip.LineSetFromConfig(&config) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | defer ls.Close() 50 | statePinNumber := ls.ByOffset(0).Number() 51 | buttonPinNumber := ls.ByOffset(2).Number() 52 | 53 | var tLast = time.Now().Add(-1 * time.Second) 54 | var halting bool 55 | go func() { 56 | time.Sleep(60 * time.Second) 57 | halting = true 58 | fmt.Println("Sending halt!") 59 | _ = ls.Halt() 60 | }() 61 | fmt.Println("Test Rotary Switch - Turn dial to test rotary encoder, press button to test it.") 62 | for { 63 | lineNumber, _, err := ls.WaitForEdge(0) 64 | if err == nil { 65 | tNow := time.Now() 66 | if (tNow.UnixMilli() - tLast.UnixMilli()) < 100 { 67 | continue 68 | } 69 | tLast = tNow 70 | if lineNumber == statePinNumber { 71 | var bits gpio.GPIOValue 72 | tDeadline := tNow.UnixNano() + 20_000_000 73 | var consecutive uint64 74 | for time.Now().UnixNano() < tDeadline { 75 | // Spin on reading the pins until we get some number 76 | // of consecutive readings that are the same. 77 | bits, _ = ls.Read(0x03) 78 | if bits&0x01 == 0x00 { 79 | // We're bouncing. 80 | consecutive = 0 81 | } else { 82 | consecutive += 1 83 | if consecutive > 25 { 84 | if bits == 0x01 { 85 | fmt.Printf("Clockwise bits=%d\n", bits) 86 | } else if bits == 0x03 { 87 | fmt.Printf("CounterClockwise bits=%d\n", bits) 88 | } 89 | break 90 | } 91 | } 92 | } 93 | } else if lineNumber == buttonPinNumber { 94 | fmt.Println("Button Pressed!") 95 | } 96 | } else { 97 | fmt.Println("Timeout detected") 98 | if halting { 99 | break 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /gpioioctl/gpio_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | // Copyright 2024 The Periph Authors. All rights reserved. 4 | // Use of this source code is governed under the Apache License, Version 2.0 5 | // that can be found in the LICENSE file. 6 | // 7 | // Create a dummy chip because ioctl is only supported on Linux 8 | 9 | package gpioioctl 10 | 11 | func init() { 12 | if len(Chips) == 0 { 13 | makeDummyChip() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gpioioctl/syscall.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | // Copyright 2024 The Periph Authors. All rights reserved. 4 | // Use of this source code is governed under the Apache License, Version 2.0 5 | // that can be found in the LICENSE file. 6 | // 7 | // This file provides a wrapper around syscall so that we can have the same source 8 | // for Windows/Linux. The problem is that in Linux syscall.Syscall takes a unintptr 9 | // as the first arg, while on windows it's a syscall.Handle. It also handles 10 | // syscall.SYS_IOCTL not being defined things besides linux. 11 | 12 | package gpioioctl 13 | 14 | import ( 15 | "syscall" 16 | ) 17 | 18 | const ( 19 | _IOCTL_FUNCTION = syscall.SYS_IOCTL 20 | ) 21 | 22 | func syscall_wrapper(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { 23 | return syscall.Syscall(trap, a1, a2, a3) 24 | } 25 | 26 | func syscall_close_wrapper(fd int) (err error) { 27 | return syscall.Close(fd) 28 | } 29 | 30 | func syscall_nonblock_wrapper(fd int, nonblocking bool) (err error) { 31 | return syscall.SetNonblock(fd, nonblocking) 32 | } 33 | -------------------------------------------------------------------------------- /gpioioctl/syscall_other.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | // Copyright 2024 The Periph Authors. All rights reserved. 4 | // Use of this source code is governed under the Apache License, Version 2.0 5 | // that can be found in the LICENSE file. 6 | package gpioioctl 7 | 8 | import ( 9 | "syscall" 10 | ) 11 | 12 | const ( 13 | _IOCTL_FUNCTION = 0 14 | ) 15 | 16 | type Errno uintptr 17 | 18 | func syscall_wrapper(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { //nolint:unused 19 | return uintptr(0), uintptr(0), syscall.ERROR_NOT_FOUND 20 | } 21 | 22 | func syscall_close_wrapper(fd int) (err error) { 23 | return syscall.Close(syscall.Handle(fd)) 24 | } 25 | 26 | func syscall_nonblock_wrapper(fd int, nonblocking bool) (err error) { 27 | return syscall.SetNonblock(syscall.Handle(fd), nonblocking) 28 | } 29 | -------------------------------------------------------------------------------- /host.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | "periph.io/x/conn/v3/driver/driverreg" 9 | // TODO(maruel): For now do not include ftdi by default. It's not stable 10 | // enough to warrant being included. 11 | // _ "periph.io/x/host/v3/ftdi" 12 | ) 13 | 14 | // Init calls driverreg.Init() and returns it as-is. 15 | // 16 | // The only difference is that by calling host.Init(), you are guaranteed to 17 | // have all the host drivers implemented in this library to be implicitly 18 | // loaded. 19 | func Init() (*driverreg.State, error) { 20 | return driverreg.Init() 21 | } 22 | -------------------------------------------------------------------------------- /host_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | // Make sure CPU and board drivers are registered. 9 | _ "periph.io/x/host/v3/allwinner" 10 | _ "periph.io/x/host/v3/am335x" 11 | _ "periph.io/x/host/v3/bcm283x" 12 | _ "periph.io/x/host/v3/beagle/bone" 13 | _ "periph.io/x/host/v3/beagle/green" 14 | _ "periph.io/x/host/v3/chip" 15 | _ "periph.io/x/host/v3/odroidc1" 16 | 17 | // While this board is ARM64, it may run ARM 32 bits binaries so load it on 18 | // 32 bits builds too. 19 | _ "periph.io/x/host/v3/pine64" 20 | _ "periph.io/x/host/v3/rpi" 21 | ) 22 | -------------------------------------------------------------------------------- /host_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | // Make sure CPU and board drivers are registered. 9 | _ "periph.io/x/host/v3/allwinner" 10 | _ "periph.io/x/host/v3/bcm283x" 11 | _ "periph.io/x/host/v3/pine64" 12 | _ "periph.io/x/host/v3/rpi" 13 | ) 14 | -------------------------------------------------------------------------------- /host_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | // Make sure required drivers are registered. 9 | _ "periph.io/x/host/v3/gpioioctl" 10 | _ "periph.io/x/host/v3/netlink" 11 | _ "periph.io/x/host/v3/sysfs" 12 | ) 13 | -------------------------------------------------------------------------------- /host_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package host 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestInit(t *testing.T) { 12 | if _, err := Init(); err != nil { 13 | t.Fatalf("failed to initialize periph: %v", err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mt7688/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package mt7688 interfaces with the MediaTek MT7688 MIPS CPU used on low cost board. 6 | package mt7688 7 | 8 | import ( 9 | "errors" 10 | 11 | "periph.io/x/host/v3/sysfs" 12 | ) 13 | 14 | // driverGPIO implements periph.Driver. 15 | type driverGPIO struct { 16 | // gpioMemory is the memory map of the CPU GPIO registers. 17 | gpioMemory *gpioMap 18 | } 19 | 20 | func (d driverGPIO) String() string { 21 | return "mt7688-gpio" 22 | } 23 | 24 | func (d driverGPIO) Prerequisites() []string { 25 | return nil 26 | } 27 | 28 | func (d driverGPIO) After() []string { 29 | return []string{"sysfs-gpio"} 30 | } 31 | 32 | func (d *driverGPIO) Init() (bool, error) { 33 | if !Present() { 34 | return false, errors.New("mt7688 board not detected") 35 | } 36 | 37 | for _, p := range cpuPins { 38 | // Initialize sysfs access right away. 39 | p.sysfsPin = sysfs.Pins[p.number] 40 | } 41 | 42 | return true, nil 43 | } 44 | 45 | var drvGPIO driverGPIO 46 | -------------------------------------------------------------------------------- /mt7688/mt7688.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package mt7688 6 | 7 | import ( 8 | "strings" 9 | 10 | "periph.io/x/conn/v3/driver/driverreg" 11 | "periph.io/x/host/v3/distro" 12 | ) 13 | 14 | // Present returns true if a mt7688 processor is detected. 15 | func Present() bool { 16 | if isMIPS { 17 | sysType, ok := distro.CPUInfo()["system type"] 18 | return ok && strings.HasPrefix(sysType, "MediaTek MT7688") 19 | } 20 | return false 21 | } 22 | 23 | func init() { 24 | // Since isMIPS is a compile time constant, the compile can strip the 25 | // unnecessary code and unused private symbols. 26 | if isMIPS { 27 | driverreg.MustRegister(&drvGPIO) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mt7688/mt7688_mips_like.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build mips || mipsle 6 | // +build mips mipsle 7 | 8 | package mt7688 9 | 10 | const isMIPS = true 11 | -------------------------------------------------------------------------------- /mt7688/mt7688_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !mipsle 6 | // +build !mipsle 7 | 8 | package mt7688 9 | 10 | const isMIPS = false 11 | -------------------------------------------------------------------------------- /nanopi/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package nanopi contains nanoPi hardware logic. 6 | // 7 | // Requires armbian jessie server. 8 | // 9 | // # Physical 10 | package nanopi 11 | -------------------------------------------------------------------------------- /nanopi/nanopi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // nanoPi pin out. 6 | 7 | package nanopi 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "strings" 13 | 14 | "periph.io/x/conn/v3/driver/driverreg" 15 | "periph.io/x/conn/v3/gpio" 16 | "periph.io/x/conn/v3/pin" 17 | "periph.io/x/conn/v3/pin/pinreg" 18 | "periph.io/x/host/v3/allwinner" 19 | "periph.io/x/host/v3/distro" 20 | ) 21 | 22 | // Present return true if a NanoPi board is detected. 23 | func Present() bool { 24 | if isArm { 25 | // This works for the NanoPi Neo Air, not sure if other NAnoPi boards 26 | // match the same DTModel prefix. 27 | return strings.HasPrefix(distro.DTModel(), "FriendlyARM") 28 | } 29 | 30 | return false 31 | } 32 | 33 | const ( 34 | boardNeoAir string = "NanoPi NEO Air" // + LTS (H2/H3 have identical pinouts) 35 | ) 36 | 37 | var ( 38 | PA1_1 pin.Pin = pin.DC_IN // VCC 3v3 Ext 39 | PA1_2 pin.Pin = pin.V5 40 | PA1_3 gpio.PinIO = allwinner.PA12 41 | PA1_4 pin.Pin = pin.V5 42 | PA1_5 gpio.PinIO = allwinner.PA11 43 | PA1_6 pin.Pin = pin.GROUND 44 | PA1_7 gpio.PinIO = allwinner.PG11 45 | PA1_8 gpio.PinIO = allwinner.PG6 46 | PA1_9 pin.Pin = pin.GROUND 47 | PA1_10 gpio.PinIO = allwinner.PG7 48 | PA1_11 gpio.PinIO = allwinner.PA0 49 | PA1_12 gpio.PinIO = allwinner.PA6 50 | PA1_13 gpio.PinIO = allwinner.PA2 51 | PA1_14 pin.Pin = pin.GROUND 52 | PA1_15 gpio.PinIO = allwinner.PA3 53 | PA1_16 gpio.PinIO = allwinner.PA8 54 | PA1_17 pin.Pin = pin.DC_IN // VCC 3v3 Ext 55 | PA1_18 gpio.PinIO = allwinner.PG9 56 | PA1_19 gpio.PinIO = allwinner.PC0 57 | PA1_20 pin.Pin = pin.GROUND 58 | PA1_21 gpio.PinIO = allwinner.PC1 59 | PA1_22 gpio.PinIO = allwinner.PA1 60 | PA1_23 gpio.PinIO = allwinner.PC2 61 | PA1_24 gpio.PinIO = allwinner.PC3 62 | 63 | FUN1_1 pin.Pin = pin.V5 64 | FUN1_2 pin.Pin = gpio.INVALID // USB-DP2 65 | FUN1_3 pin.Pin = gpio.INVALID // USB-DM2 66 | FUN1_4 pin.Pin = gpio.INVALID // USB-DP3 67 | FUN1_5 pin.Pin = gpio.INVALID // USB-DM3 68 | FUN1_6 pin.Pin = allwinner.PL11 // IR-RX 69 | FUN1_7 pin.Pin = allwinner.PA17 // SPDIF-OUT 70 | FUN1_8 pin.Pin = allwinner.PA18 // PCM0_SYNC/I2S0_LRC 71 | FUN1_9 pin.Pin = allwinner.PA19 // PCM0_CLK/I2S0_BCK 72 | FUN1_10 pin.Pin = allwinner.PA20 // PCM0_DOUT/I2S0_SDOUT 73 | FUN1_11 pin.Pin = allwinner.PA21 // PCM0_DIN/I2S0_SDIN 74 | FUN1_12 pin.Pin = pin.GROUND // 75 | 76 | FUN2_1 pin.Pin = allwinner.HP_LEFT // LINEOUTL 77 | FUN2_2 pin.Pin = allwinner.HP_RIGHT // LINEOUT 78 | FUN2_3 pin.Pin = allwinner.MIC_GND // MIC GND 79 | FUN2_4 pin.Pin = allwinner.MIC_IN // MIC IN 80 | 81 | FUN3_1 pin.Pin = pin.GROUND 82 | FUN3_2 pin.Pin = pin.V5 83 | FUN3_3 pin.Pin = allwinner.PA4 // UART TX 84 | FUN3_4 pin.Pin = allwinner.PA5 // UART RX 85 | 86 | ) 87 | 88 | // registerHeaders registers the headers for various NanoPi boards. Currently 89 | // only NanoPi NEO Air is supported. 90 | func registerHeaders(model string) error { 91 | if strings.Contains(model, boardNeoAir) { 92 | // 24pin expansion port 93 | if err := pinreg.Register("PA", [][]pin.Pin{ 94 | {PA1_1, PA1_2}, 95 | {PA1_3, PA1_4}, 96 | {PA1_5, PA1_6}, 97 | {PA1_7, PA1_8}, 98 | {PA1_9, PA1_10}, 99 | {PA1_11, PA1_12}, 100 | {PA1_13, PA1_14}, 101 | {PA1_15, PA1_16}, 102 | {PA1_17, PA1_18}, 103 | {PA1_19, PA1_20}, 104 | {PA1_21, PA1_22}, 105 | {PA1_23, PA1_24}, 106 | }); err != nil { 107 | return err 108 | } 109 | 110 | // 12pin function interface 111 | if err := pinreg.Register("FUN", [][]pin.Pin{ 112 | {FUN1_1}, 113 | {FUN1_2}, 114 | {FUN1_3}, 115 | {FUN1_4}, 116 | {FUN1_5}, 117 | {FUN1_6}, 118 | {FUN1_7}, 119 | {FUN1_8}, 120 | {FUN1_9}, 121 | {FUN1_10}, 122 | {FUN1_11}, 123 | {FUN1_12}, 124 | {FUN2_1}, 125 | {FUN2_2}, 126 | {FUN2_3}, 127 | {FUN2_4}, 128 | {FUN3_1}, 129 | {FUN3_2}, 130 | {FUN3_3}, 131 | {FUN3_4}, 132 | }); err != nil { 133 | return err 134 | } 135 | } 136 | 137 | return nil 138 | } 139 | 140 | // driver implements periph.Driver. 141 | type driver struct { 142 | } 143 | 144 | // String is the text representation of the board. 145 | func (d *driver) String() string { 146 | return "nanopi" 147 | } 148 | 149 | // Prerequisites load drivers before the actual driver is loaded. For 150 | // these boards, we do not need any prerequisites. 151 | func (d *driver) Prerequisites() []string { 152 | return nil 153 | } 154 | 155 | // After this driver is loaded, we need to load generic Allwinner drivers 156 | // for the GPIO pins which are identical on all Allwinner CPUs. 157 | func (d *driver) After() []string { 158 | return []string{"allwinner-gpio", "allwinner-gpio-pl"} 159 | } 160 | 161 | // Init initializes the driver by checking its presence and if found, the 162 | // driver will be registered. 163 | func (d *driver) Init() (bool, error) { 164 | if !Present() { 165 | return false, errors.New("NanoPi board not detected") 166 | } 167 | 168 | model := distro.DTModel() 169 | if model == "" { 170 | return true, fmt.Errorf("nanopi: failed to obtain model") 171 | } 172 | 173 | err := registerHeaders(model) 174 | return true, err 175 | } 176 | 177 | // init register the driver. 178 | func init() { 179 | if isArm { 180 | driverreg.MustRegister(&drv) 181 | } 182 | } 183 | 184 | var drv driver 185 | -------------------------------------------------------------------------------- /nanopi/nanopi_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package nanopi 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /nanopi/nanopi_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build arm64 6 | // +build arm64 7 | 8 | package nanopi 9 | 10 | const isArm = true 11 | -------------------------------------------------------------------------------- /nanopi/nanopi_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm && !arm64 6 | // +build !arm,!arm64 7 | 8 | package nanopi 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /netlink/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package netlink implements host drivers based on the Linux netlink connector 6 | // interface. See 7 | // https://www.kernel.org/doc/Documentation/connector/connector.txt for details. 8 | package netlink 9 | -------------------------------------------------------------------------------- /netlink/netlink_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package netlink 6 | 7 | import ( 8 | "fmt" 9 | "path/filepath" 10 | "syscall" 11 | ) 12 | 13 | const isLinux = true 14 | 15 | // connSocket is a simple wrapper around a Linux netlink connector socket. 16 | type connSocket struct { 17 | fd int 18 | } 19 | 20 | // newConnSocket returns a socket instance. 21 | func newConnSocket() (*connSocket, error) { 22 | // Open netlink socket. 23 | fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_CONNECTOR) 24 | if err != nil { 25 | return nil, fmt.Errorf("failed to open netlink socket: %v", err) 26 | } 27 | 28 | if err := syscall.Bind(fd, &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}); err != nil { 29 | return nil, fmt.Errorf("failed to bind netlink socket: %v", err) 30 | } 31 | 32 | return &connSocket{fd: fd}, nil 33 | } 34 | 35 | // send writes w to the socket. 36 | func (s *connSocket) send(w []byte) error { 37 | return syscall.Sendto(s.fd, w, 0, &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}) 38 | } 39 | 40 | // recv reads at most len(r) bytes from the socket into r. Returns the actually 41 | // read number of bytes. 42 | func (s *connSocket) recv(r []byte) (int, error) { 43 | n, _, err := syscall.Recvfrom(s.fd, r, 0) 44 | if err != nil { 45 | return 0, err 46 | } 47 | return n, nil 48 | } 49 | 50 | // close closes the socket. 51 | func (s *connSocket) close() error { 52 | fd := s.fd 53 | s.fd = 0 54 | return syscall.Close(fd) 55 | } 56 | 57 | // isOneWireAvailable checks to see if the Linux onewire bus drivers are loaded. 58 | // It does this by looking for entries in the /sys/bus pseudo-filesystem. 59 | // 60 | // On a Raspberry Pi SBC, the onewire bus is enabled by creating entries in the 61 | // kernel config.txt file which is located in /boot, or /boot/firmware depending 62 | // on OS/kernel levels. 63 | func isOneWireAvailable() bool { 64 | items, err := filepath.Glob("/sys/bus/w1/devices/*") 65 | if err != nil || len(items) == 0 { 66 | return false 67 | } 68 | return true 69 | } 70 | -------------------------------------------------------------------------------- /netlink/netlink_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !linux 6 | // +build !linux 7 | 8 | package netlink 9 | 10 | import "errors" 11 | 12 | const isLinux = false 13 | 14 | type connSocket struct{} 15 | 16 | func newConnSocket() (*connSocket, error) { 17 | return nil, errors.New("netlink sockets are not supported") 18 | } 19 | 20 | func (*connSocket) send(_ []byte) error { 21 | return errors.New("not implemented") 22 | } 23 | 24 | func (*connSocket) recv(_ []byte) (int, error) { 25 | return 0, errors.New("not implemented") 26 | } 27 | 28 | func (*connSocket) close() error { 29 | return errors.New("not implemented") 30 | } 31 | 32 | func isOneWireAvailable() bool { 33 | return false 34 | } 35 | -------------------------------------------------------------------------------- /odroidc1/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package odroidc1 contains header definitions for Hardkernel's ODROID C0, C1, 6 | // and C1+ boards. 7 | // 8 | // These boards use an Amlogic S805 processor (called "meson_8b" in the linux 9 | // kernel). Currently no package for memory-mapped I/O has been written for 10 | // this processor, thus all gpio functions are implemented via sysfs. 11 | // 12 | // This package only exports the main J2 header, which is rPi compatible except 13 | // for a couple of analog pins (which are not currently supported). The J2 14 | // header has two I²C buses on header pins 3/5 and 27/28, the I²C functionality 15 | // can be enabled by loading the aml_i2c kernel module. It has one SPI bus on 16 | // header pins 19/21/23/24. The onewire gpio driver appears to be loaded by 17 | // default on header pin 7. 18 | // 19 | // # References 20 | // 21 | // Product page: http://www.hardkernel.com/main/products/prdt_info.php?g_code=G143703355573&tab_idx=2 22 | // 23 | // Hardware wiki: http://odroid.com/dokuwiki/doku.php?id=en:c1_hardware 24 | // 25 | // Ubuntu drivers: http://odroid.com/dokuwiki/doku.php?id=en:odroid-c1#ubuntu 26 | package odroidc1 27 | -------------------------------------------------------------------------------- /odroidc1/odroidc1_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The PIO Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package odroidc1 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /odroidc1/odroidc1_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The PIO Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package odroidc1 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /odroidc1/odroidc1smoketest/odroidc1smoketest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package odroidc1smoketest is leveraged by periph-smoketest to verify that 6 | // basic ODROID-C1 specific functionality works. 7 | package odroidc1smoketest 8 | 9 | import ( 10 | "errors" 11 | "flag" 12 | "fmt" 13 | "sort" 14 | "strconv" 15 | 16 | "periph.io/x/conn/v3/gpio" 17 | "periph.io/x/conn/v3/gpio/gpioreg" 18 | "periph.io/x/conn/v3/pin/pinreg" 19 | "periph.io/x/host/v3/odroidc1" 20 | ) 21 | 22 | // SmokeTest is imported by periph-smoketest. 23 | type SmokeTest struct { 24 | } 25 | 26 | func (s *SmokeTest) String() string { 27 | return s.Name() 28 | } 29 | 30 | // Name implements periph-smoketest.SmokeTest. 31 | func (s *SmokeTest) Name() string { 32 | return "odroid-c1" 33 | } 34 | 35 | // Description implements periph-smoketest.SmokeTest. 36 | func (s *SmokeTest) Description() string { 37 | return "Quad core low cost board made by hardkernel.com" 38 | } 39 | 40 | // Run implements periph-smoketest.SmokeTest. 41 | func (s *SmokeTest) Run(f *flag.FlagSet, args []string) error { 42 | if err := f.Parse(args); err != nil { 43 | return err 44 | } 45 | if f.NArg() != 0 { 46 | f.Usage() 47 | return errors.New("unrecognized arguments") 48 | } 49 | if !odroidc1.Present() { 50 | f.Usage() 51 | return errors.New("this smoke test can only be run on an ODROID-C1 based host") 52 | } 53 | // TODO: add amlogic s805 detection check once that is implemented. 54 | tests := []func() error{ 55 | testOdroidC1Headers, testOdroidC1GpioNames, testOdroidC1Aliases, 56 | } 57 | for _, t := range tests { 58 | if err := t(); err != nil { 59 | return err 60 | } 61 | } 62 | return nil 63 | } 64 | 65 | // testOdroidC1Headers verifies that the appropriate headers with the right pin 66 | // count show up and point checks that a couple of pins are correct. 67 | func testOdroidC1Headers() error { 68 | h := pinreg.All() 69 | if len(h) != 1 { 70 | return fmt.Errorf("expected to find 1 header, not %d", len(h)) 71 | } 72 | if len(h["J2"]) != 20 { 73 | return fmt.Errorf("expected J2 to have 20 rows, not %d", len(h["J2"])) 74 | } 75 | 76 | for r := range h["J2"] { 77 | if len(h["J2"][r]) != 2 { 78 | return fmt.Errorf("expected row %d of J2 to have 2 pins, not %d", 79 | r, len(h["J2"][r])) 80 | } 81 | } 82 | 83 | j2_3 := h["J2"][1][0] 84 | if j2_3.Name() != "GPIO74" { 85 | return fmt.Errorf("expected J2_3 to be GPIO74, not %s", j2_3.Name()) 86 | } 87 | p := gpioreg.ByName("GPIO74") 88 | if p == nil || p.Name() != j2_3.Name() { // p is gpio.PinIO while j2_3 is pins.Pin 89 | return fmt.Errorf(`expected gpioreg.ByName("GPIO74") to equal h["J2"][1][0], instead `+ 90 | "got %s and %s", p, j2_3) 91 | } 92 | 93 | return nil 94 | } 95 | 96 | // testOdroidC1GpioNumbers tests that the gpio pins get the right numbers. 97 | func testOdroidC1GpioNumbers() error { 98 | must := map[int]string{74: "I2CA_SDA", 75: "I2CA_SCL", 76: "I2CB_SDA", 77: "I2C_SCL", 99 | 107: "SPI0_MOSI", 106: "SPI0_MISO", 105: "SPI0_CLK", 117: "SPI0_CS0"} 100 | for number, name := range must { 101 | pin := gpioreg.ByName(strconv.Itoa(number)) 102 | if pin == nil { 103 | return fmt.Errorf("could not get gpio pin %d (should be %s)", number, name) 104 | } 105 | if pin.Name() != name { 106 | return fmt.Errorf("expected gpio pin %d to be %s but it's %s", 107 | number, name, pin.Name()) 108 | } 109 | } 110 | return nil 111 | } 112 | 113 | // testOdroidC1GpioNames tests that the gpio pins get the right names. 114 | func testOdroidC1GpioNames() error { 115 | all := []string{} 116 | for _, p := range gpioreg.All() { 117 | all = append(all, p.Name()) 118 | } 119 | sort.Strings(all) 120 | 121 | must := []string{"GPIO74", "GPIO118"} 122 | for _, name := range must { 123 | ix := sort.SearchStrings(all, name) 124 | if ix >= len(all) || all[ix] != name { 125 | return fmt.Errorf("expected to find gpio pin %s but it's missing", name) 126 | } 127 | } 128 | return nil 129 | } 130 | 131 | // testOdroidC1Aliases tests that the various gpio pin aliases get set-up 132 | func testOdroidC1Aliases() error { 133 | tests := map[string]string{ // alias->real 134 | "I2CA_SDA": "GPIO74", 135 | "I2CA_SCL": "GPIO75", 136 | "I2CB_SDA": "GPIO76", 137 | "I2CB_SCL": "GPIO77", 138 | "SPI0_MOSI": "GPIO107", // Amlogic S805: "GPIO107": "X10", 139 | "SPI0_MISO": "GPIO106", // Amlogic S805: "GPIO106": "X9", 140 | "SPI0_CLK": "GPIO105", // Amlogic S805: "GPIO105": "X8", 141 | "SPI0_CS0": "GPIO117", // Amlogic S805: "GPIO117": "X20", 142 | } 143 | for a, r := range tests { 144 | p := gpioreg.ByName(a) 145 | if p == nil { 146 | return fmt.Errorf("failed to open %s", a) 147 | } 148 | pa, ok := p.(gpio.RealPin) 149 | if !ok { 150 | return fmt.Errorf("expected that pin %s is an alias, not %T", a, p) 151 | } 152 | if pr := pa.Real(); pr.Name() != r { 153 | return fmt.Errorf("expected that alias %s have real pin %s but it's %s", 154 | a, r, pr.Name()) 155 | } 156 | } 157 | return nil 158 | } 159 | -------------------------------------------------------------------------------- /orangepi/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package orangepi contains Orange Pi hardware logic. 6 | // 7 | // Requires armbian jessie server. 8 | // 9 | // # Physical 10 | // 11 | // http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/index.html 12 | package orangepi 13 | -------------------------------------------------------------------------------- /orangepi/orangepi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Orange Pi pin out. 6 | 7 | package orangepi 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "strings" 13 | 14 | "periph.io/x/conn/v3/driver/driverreg" 15 | "periph.io/x/conn/v3/gpio" 16 | "periph.io/x/conn/v3/pin" 17 | "periph.io/x/conn/v3/pin/pinreg" 18 | "periph.io/x/host/v3/allwinner" 19 | "periph.io/x/host/v3/distro" 20 | ) 21 | 22 | // Present return true if a Orange Pi board is detected. 23 | func Present() bool { 24 | if isArm { 25 | // This works for the Orange Pi Zero, not sure if other Orange Pi boards 26 | // match the same DTModel prefix. 27 | return strings.HasPrefix(distro.DTModel(), "OrangePi") 28 | } 29 | 30 | return false 31 | } 32 | 33 | const ( 34 | boardZero string = "Orange Pi Zero" // + LTS (H2/H3 have identical pinouts) 35 | ) 36 | 37 | var ( 38 | PA1_1 pin.Pin = pin.DC_IN // VCC 3v3 Ext 39 | PA1_2 pin.Pin = pin.V5 40 | PA1_3 gpio.PinIO = allwinner.PA12 41 | PA1_4 pin.Pin = pin.V5 42 | PA1_5 gpio.PinIO = allwinner.PA11 43 | PA1_6 pin.Pin = pin.GROUND 44 | PA1_7 gpio.PinIO = allwinner.PA6 45 | PA1_8 gpio.PinIO = allwinner.PG6 46 | PA1_9 pin.Pin = pin.GROUND 47 | PA1_10 gpio.PinIO = allwinner.PG7 48 | PA1_11 gpio.PinIO = allwinner.PA1 49 | PA1_12 gpio.PinIO = allwinner.PA7 50 | PA1_13 gpio.PinIO = allwinner.PA0 51 | PA1_14 pin.Pin = pin.GROUND 52 | PA1_15 gpio.PinIO = allwinner.PA3 53 | PA1_16 gpio.PinIO = allwinner.PA19 54 | PA1_17 pin.Pin = pin.DC_IN // VCC 3v3 Ext 55 | PA1_18 gpio.PinIO = allwinner.PA18 56 | PA1_19 gpio.PinIO = allwinner.PA15 57 | PA1_20 pin.Pin = pin.GROUND 58 | PA1_21 gpio.PinIO = allwinner.PA16 59 | PA1_22 gpio.PinIO = allwinner.PA2 60 | PA1_23 gpio.PinIO = allwinner.PA14 61 | PA1_24 gpio.PinIO = allwinner.PA13 62 | PA1_25 pin.Pin = pin.GROUND 63 | PA1_26 gpio.PinIO = allwinner.PA10 64 | 65 | FUN1_1 pin.Pin = pin.V5 66 | FUN1_2 pin.Pin = pin.GROUND 67 | FUN1_3 pin.Pin = gpio.INVALID // USB-DM2 68 | FUN1_4 pin.Pin = gpio.INVALID // USB-DP2 69 | FUN1_5 pin.Pin = gpio.INVALID // USB-DM3 70 | FUN1_6 pin.Pin = gpio.INVALID // USB-DP3 71 | FUN1_7 pin.Pin = allwinner.HP_RIGHT // LINEOUTR 72 | FUN1_8 pin.Pin = allwinner.HP_LEFT // LINEOUTL 73 | FUN1_9 pin.Pin = gpio.INVALID // TVOUT 74 | FUN1_10 pin.Pin = gpio.INVALID // MBIAS, Bias Voltage output for mic 75 | FUN1_11 pin.Pin = allwinner.MIC_IN // INPUT Analog Microphone pin (+) 76 | FUN1_12 pin.Pin = allwinner.MIC_GND // INPUT Analog Microphone pin (-) 77 | FUN1_13 pin.Pin = allwinner.PL11 // IR-RX 78 | ) 79 | 80 | // registerHeaders registers the headers for various Orange Pi boards. Currently 81 | // only Orange Pi Zero is supported. 82 | func registerHeaders(model string) error { 83 | // http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero.html 84 | if strings.Contains(model, boardZero) { 85 | // 26pin expansion port 86 | if err := pinreg.Register("PA", [][]pin.Pin{ 87 | {PA1_1, PA1_2}, 88 | {PA1_3, PA1_4}, 89 | {PA1_5, PA1_6}, 90 | {PA1_7, PA1_8}, 91 | {PA1_9, PA1_10}, 92 | {PA1_11, PA1_12}, 93 | {PA1_13, PA1_14}, 94 | {PA1_15, PA1_16}, 95 | {PA1_17, PA1_18}, 96 | {PA1_19, PA1_20}, 97 | {PA1_21, PA1_22}, 98 | {PA1_23, PA1_24}, 99 | {PA1_25, PA1_26}, 100 | }); err != nil { 101 | return err 102 | } 103 | 104 | // 13pin function interface 105 | if err := pinreg.Register("FUN", [][]pin.Pin{ 106 | {FUN1_1}, 107 | {FUN1_2}, 108 | {FUN1_3}, 109 | {FUN1_4}, 110 | {FUN1_5}, 111 | {FUN1_6}, 112 | {FUN1_7}, 113 | {FUN1_8}, 114 | {FUN1_9}, 115 | {FUN1_10}, 116 | {FUN1_11}, 117 | {FUN1_12}, 118 | {FUN1_13}, 119 | }); err != nil { 120 | return err 121 | } 122 | } 123 | 124 | return nil 125 | } 126 | 127 | // driver implements periph.Driver. 128 | type driver struct { 129 | } 130 | 131 | // String is the text representation of the board. 132 | func (d *driver) String() string { 133 | return "orangepi" 134 | } 135 | 136 | // Prerequisites load drivers before the actual driver is loaded. For 137 | // these boards, we do not need any prerequisites. 138 | func (d *driver) Prerequisites() []string { 139 | return nil 140 | } 141 | 142 | // After this driver is loaded, we need to load generic Allwinner drivers 143 | // for the GPIO pins which are identical on all Allwinner CPUs. 144 | func (d *driver) After() []string { 145 | return []string{"allwinner-gpio", "allwinner-gpio-pl"} 146 | } 147 | 148 | // Init initializes the driver by checking its presence and if found, the 149 | // driver will be registered. 150 | func (d *driver) Init() (bool, error) { 151 | if !Present() { 152 | return false, errors.New("borad Orange Pi not detected") 153 | } 154 | 155 | model := distro.DTModel() 156 | if model == "" { 157 | return true, fmt.Errorf("orangepi: failed to obtain model") 158 | } 159 | 160 | err := registerHeaders(model) 161 | return true, err 162 | } 163 | 164 | // init register the driver. 165 | func init() { 166 | if isArm { 167 | driverreg.MustRegister(&drv) 168 | } 169 | } 170 | 171 | var drv driver 172 | -------------------------------------------------------------------------------- /orangepi/orangepi_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package orangepi 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /orangepi/orangepi_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build arm64 6 | // +build arm64 7 | 8 | package orangepi 9 | 10 | const isArm = true 11 | -------------------------------------------------------------------------------- /orangepi/orangepi_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm && !arm64 6 | // +build !arm,!arm64 7 | 8 | package orangepi 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /pine64/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package pine64 contains Pine64 hardware logic. It is intrinsically 6 | // related to package a64. 7 | // 8 | // Requires Armbian Jessie Server. 9 | // 10 | // # Physical 11 | // 12 | // http://files.pine64.org/doc/Pine%20A64%20Schematic/Pine%20A64%20Pin%20Assignment%20160119.pdf 13 | // 14 | // http://wiki.pine64.org/images/2/2e/Pine64_Board_Connector_heatsink.png 15 | package pine64 16 | -------------------------------------------------------------------------------- /pine64/pine64_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pine64 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /pine64/pine64_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build arm64 6 | // +build arm64 7 | 8 | package pine64 9 | 10 | const isArm = true 11 | -------------------------------------------------------------------------------- /pine64/pine64_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm && !arm64 6 | // +build !arm,!arm64 7 | 8 | package pine64 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /pmem/alloc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem 6 | 7 | import ( 8 | "os" 9 | "testing" 10 | ) 11 | 12 | func TestAlloc_fail(t *testing.T) { 13 | defer reset() 14 | if m, err := Alloc(0); m != nil || err == nil { 15 | t.Fatal("0 bytes") 16 | } 17 | if m, err := Alloc(1); m != nil || err == nil { 18 | t.Fatal("not 4096 bytes") 19 | } 20 | if m, err := Alloc(4096); m != nil || err == nil { 21 | t.Fatal("ReadPageMap() fails") 22 | } 23 | } 24 | 25 | func TestAlloLinuxc_fail(t *testing.T) { 26 | if m, err := allocLinux(8192); m != nil || err == nil { 27 | t.Fatal("only 4096 is supported") 28 | } 29 | } 30 | 31 | func TestAlloc_no_high_bit(t *testing.T) { 32 | defer reset() 33 | openFile = func(path string, flag int) (fileIO, error) { 34 | if path != "/proc/self/pagemap" { 35 | t.Fatal(path) 36 | } 37 | if flag != os.O_RDONLY|os.O_SYNC { 38 | t.Fatal(flag) 39 | } 40 | return &simpleFile{data: []byte{1, 2, 3, 4, 5, 6, 7, 8}}, nil 41 | } 42 | if m, err := allocLinux(4096); m != nil || err == nil { 43 | t.Fatal("page not physically mapped") 44 | } 45 | } 46 | 47 | func TestAlloc(t *testing.T) { 48 | defer reset() 49 | openFile = func(path string, flag int) (fileIO, error) { 50 | if path != "/proc/self/pagemap" { 51 | t.Fatal(path) 52 | } 53 | if flag != os.O_RDONLY|os.O_SYNC { 54 | t.Fatal(flag) 55 | } 56 | return &simpleFile{data: []byte{1, 2, 3, 4, 5, 6, 7, 0x80}}, nil 57 | } 58 | _, err := allocLinux(4096) 59 | if isLinux && !isWSL() { 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | } else { 64 | if err == nil { 65 | t.Fatal("syscall.Mlock() is not implemented") 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pmem/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package pmem implements handling of physical memory for user space programs. 6 | // 7 | // To make things confusing, a modern computer has many view of the memory 8 | // (address spaces): 9 | // 10 | // # User 11 | // 12 | // User mode address space is the virtual address space that an application 13 | // runs in. It is generally a tad less than half the addressable memory, so on 14 | // a 32 bits system, the addressable range is 1.9Gb. For 64 bits OS, it depends 15 | // but it usually at least 3.5Gb. The memory is virtual and can be flushed to 16 | // disk in the swap file unless individual pages are locked. 17 | // 18 | // # Kernel 19 | // 20 | // Kernel address space is the virtual address space the kernel sees. It often 21 | // can see the currently active user space program on the current CPU core in 22 | // addition to all the memory the kernel sees. The kernel memory pages that are 23 | // not mlock()'ed are 'virtual' and can be flushed to disk in the swap file 24 | // when there's not enough RAM available. On linux systems, the kernel 25 | // addressed memory can be mapped in user space via `/dev/kmem`. 26 | // 27 | // # Physical 28 | // 29 | // Physical memory address space is the actual address of each page in the DRAM 30 | // chip and anything connected to the memory controller. The mapping may be 31 | // different depending on what controller looks at the bus, like with IOMMU. So 32 | // a peripheral (GPU, DMA controller) may have a different view of the physical 33 | // memory than the host CPU. On linux systems, this memory can be mapped in 34 | // user space via `/dev/mem`. 35 | // 36 | // # CPU 37 | // 38 | // The CPU or its subsystems may memory map registers (for example, to control 39 | // GPIO pins, clock speed, etc). This is not "real" memory, this is a view of 40 | // registers but it still follows "mostly" the same semantic as DRAM backed 41 | // physical memory. 42 | // 43 | // Some CPU memory may have very special semantic where the mere fact of 44 | // reading has side effects. For example reading a specific register may 45 | // latches another. 46 | // 47 | // CPU memory accesses are layered with multiple caches, usually named L1, L2 48 | // and optionally L3. Some controllers (DMA) can see some cache levels (L2) but 49 | // not others (L1) on some CPU architecture (bcm283x). This means that a user 50 | // space program writing data to a memory page and immediately asking the DMA 51 | // controller to read it may cause stale data to be read! 52 | // 53 | // # Hypervisor 54 | // 55 | // Hypervisor can change the complete memory mapping as seen by the kernel. 56 | // This is outside the scope of this project. :) 57 | // 58 | // # Summary 59 | // 60 | // In practice, the semantics change between CPU manufacturers (Broadcom vs 61 | // Allwinner) and between architectures (ARM vs x86). The most tricky one is to 62 | // understand cached memory and how it affects coherence and performance. 63 | // Uncached memory is extremely slow so it must only be used when necessary. 64 | // 65 | // # References 66 | // 67 | // Overview of IOMMU: 68 | // https://en.wikipedia.org/wiki/Input-output_memory_management_unit 69 | package pmem 70 | -------------------------------------------------------------------------------- /pmem/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem_test 6 | 7 | import ( 8 | "log" 9 | 10 | "periph.io/x/host/v3/pmem" 11 | ) 12 | 13 | func ExampleMapAsPOD() { 14 | // Let's say the CPU has 4 x 32 bits memory mapped registers at the address 15 | // 0xDEADBEEF. 16 | var reg *[4]uint32 17 | if err := pmem.MapAsPOD(0xDEADBEAF, ®); err != nil { 18 | log.Fatal(err) 19 | } 20 | // reg now points to physical memory. 21 | } 22 | -------------------------------------------------------------------------------- /pmem/mem_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem 6 | 7 | import "syscall" 8 | 9 | const isLinux = true 10 | 11 | func mmap(fd uintptr, offset int64, length int) ([]byte, error) { 12 | v, err := syscall.Mmap(int(fd), offset, length, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) 13 | if err != nil { 14 | return nil, wrapf("failed to memory map: %v", err) 15 | } 16 | return v, nil 17 | } 18 | 19 | func munmap(b []byte) error { 20 | if err := syscall.Munmap(b); err != nil { 21 | return wrapf("failed to unmap memory: %v", err) 22 | } 23 | return nil 24 | 25 | } 26 | 27 | func mlock(b []byte) error { 28 | if err := syscall.Mlock(b); err != nil { 29 | return wrapf("failed to lock memory: %v", err) 30 | } 31 | return nil 32 | } 33 | 34 | func munlock(b []byte) error { 35 | if err := syscall.Munlock(b); err != nil { 36 | return wrapf("failed to unlock memory: %v", err) 37 | } 38 | return nil 39 | } 40 | 41 | // uallocMem allocates user space memory. 42 | func uallocMem(size int) ([]byte, error) { 43 | b, err := syscall.Mmap( 44 | 0, 45 | 0, 46 | size, 47 | syscall.PROT_READ|syscall.PROT_WRITE, 48 | syscall.MAP_ANONYMOUS|syscall.MAP_LOCKED|syscall.MAP_NORESERVE|syscall.MAP_SHARED) 49 | // syscall.MAP_HUGETLB / MAP_HUGE_2MB 50 | // See /sys/kernel/mm/hugepages but both C.H.I.P. running Jessie and Raspbian 51 | // Jessie do not expose huge pages. :( 52 | if err != nil { 53 | return nil, wrapf("allocating %d bytes failed: %v", size, err) 54 | } 55 | return b, err 56 | } 57 | -------------------------------------------------------------------------------- /pmem/mem_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !linux 6 | // +build !linux 7 | 8 | package pmem 9 | 10 | const isLinux = false 11 | 12 | func mmap(fd uintptr, offset int64, length int) ([]byte, error) { 13 | return nil, wrapf("syscall.Mmap() not implemented on this OS") 14 | } 15 | 16 | func munmap(b []byte) error { 17 | return wrapf("syscall.Munmap() not implemented on this OS") 18 | } 19 | 20 | func mlock(b []byte) error { 21 | return wrapf("syscall.Mlock() not implemented on this OS") 22 | } 23 | 24 | func munlock(b []byte) error { 25 | return wrapf("syscall.Munlock() not implemented on this OS") 26 | } 27 | 28 | // uallocMem allocates user space memory. 29 | func uallocMem(size int) ([]byte, error) { 30 | return make([]byte, size), nil 31 | } 32 | -------------------------------------------------------------------------------- /pmem/pagemap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem 6 | 7 | import ( 8 | "encoding/binary" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "os" 13 | ) 14 | 15 | // ReadPageMap reads a physical address mapping for a virtual page address from 16 | // /proc/self/pagemap. 17 | // 18 | // It returns the physical address that corresponds to the start of the virtual 19 | // page within which the virtual address virtAddr is located. 20 | // 21 | // The meaning of the return value is documented at 22 | // https://www.kernel.org/doc/Documentation/vm/pagemap.txt 23 | func ReadPageMap(virtAddr uintptr) (uint64, error) { 24 | if !isLinux || isWSL() { 25 | return 0, errors.New("pmem: pagemap is not supported on this platform") 26 | } 27 | return readPageMapLinux(virtAddr) 28 | } 29 | 30 | // 31 | 32 | var ( 33 | pageMap fileIO 34 | pageMapErr error 35 | ) 36 | 37 | func readPageMapLinux(virtAddr uintptr) (uint64, error) { 38 | var b [8]byte 39 | mu.Lock() 40 | defer mu.Unlock() 41 | if pageMap == nil && pageMapErr == nil { 42 | // Open /proc/self/pagemap. 43 | // 44 | // It is a uint64 array where the index represents the virtual 4Kb page 45 | // number and the value represents the physical page properties backing 46 | // this virtual page. 47 | pageMap, pageMapErr = openFile("/proc/self/pagemap", os.O_RDONLY|os.O_SYNC) 48 | } 49 | if pageMapErr != nil { 50 | return 0, pageMapErr 51 | } 52 | // Convert address to page number, then index in uint64 array. 53 | offset := int64(virtAddr / pageSize * 8) 54 | if _, err := pageMap.Seek(offset, io.SeekStart); err != nil { 55 | return 0, fmt.Errorf("pmem: failed to seek at 0x%x for 0x%x: %v", offset, virtAddr, err) 56 | } 57 | n, err := pageMap.Read(b[:]) 58 | if err != nil { 59 | return 0, fmt.Errorf("pmem: failed to read at 0x%x for 0x%x: %v", offset, virtAddr, err) 60 | } 61 | if n != len(b) { 62 | return 0, fmt.Errorf("pmem: failed to read the amount of data %d", len(b)) 63 | } 64 | return binary.LittleEndian.Uint64(b[:]), nil 65 | } 66 | -------------------------------------------------------------------------------- /pmem/pagemap_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem 6 | 7 | import ( 8 | "os" 9 | "testing" 10 | ) 11 | 12 | func TestReadPageMap_fail(t *testing.T) { 13 | defer reset() 14 | if u, err := ReadPageMap(8192); u != 0 || err == nil { 15 | t.Fatal("can't open file") 16 | } 17 | } 18 | 19 | func TestReadPageMap(t *testing.T) { 20 | defer reset() 21 | openFile = func(path string, flag int) (fileIO, error) { 22 | if path != "/proc/self/pagemap" { 23 | t.Fatal(path) 24 | } 25 | if flag != os.O_RDONLY|os.O_SYNC { 26 | t.Fatal(flag) 27 | } 28 | return &simpleFile{data: []byte{1, 2, 3, 4, 5, 6, 7, 8}}, nil 29 | } 30 | u, err := readPageMapLinux(8192) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | if u != 0x807060504030201 { 35 | t.Fatal(u) 36 | } 37 | } 38 | 39 | func TestReadPageMap_short(t *testing.T) { 40 | defer reset() 41 | openFile = func(path string, flag int) (fileIO, error) { 42 | if path != "/proc/self/pagemap" { 43 | t.Fatal(path) 44 | } 45 | if flag != os.O_RDONLY|os.O_SYNC { 46 | t.Fatal(flag) 47 | } 48 | return &simpleFile{data: []byte{1, 2}}, nil 49 | } 50 | if u, err := readPageMapLinux(8192); u != 0 || err == nil { 51 | t.Fatal("didn't read 8 bytes") 52 | } 53 | } 54 | 55 | func TestReadPageMap_read_fail(t *testing.T) { 56 | defer reset() 57 | openFile = func(path string, flag int) (fileIO, error) { 58 | if path != "/proc/self/pagemap" { 59 | t.Fatal(path) 60 | } 61 | if flag != os.O_RDONLY|os.O_SYNC { 62 | t.Fatal(flag) 63 | } 64 | return &simpleFile{}, nil 65 | } 66 | if u, err := readPageMapLinux(8192); u != 0 || err == nil { 67 | t.Fatal("Read() failed") 68 | } 69 | } 70 | 71 | func TestReadPageMap_seek_fail(t *testing.T) { 72 | defer reset() 73 | openFile = func(path string, flag int) (fileIO, error) { 74 | if path != "/proc/self/pagemap" { 75 | t.Fatal(path) 76 | } 77 | if flag != os.O_RDONLY|os.O_SYNC { 78 | t.Fatal(flag) 79 | } 80 | return &failFile{}, nil 81 | } 82 | if u, err := readPageMapLinux(8192); u != 0 || err == nil { 83 | t.Fatal("Seek() failed") 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /pmem/smoketest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem 6 | 7 | import ( 8 | "bytes" 9 | "math/rand" 10 | ) 11 | 12 | // TestCopy is used by CPU drivers to verify that the DMA engine works 13 | // correctly. 14 | // 15 | // It is not meant to be used by end users. 16 | // 17 | // TestCopy allocates two buffer via `alloc`, once as the source and one as the 18 | // destination. It fills the source with random data and the destination with 19 | // 0x11. 20 | // 21 | // `copyMem` is expected to copy the memory from pSrc to pDst, with an offset 22 | // of `hole` and size `size-2*hole`. 23 | // 24 | // The function `copyMem` being tested is only given the buffer physical 25 | // addresses and must copy the data without other help. It is expected to 26 | // 27 | // This confirm misaligned DMA copying works. 28 | // leverage the host's DMA engine. 29 | func TestCopy(size, holeSize int, alloc func(size int) (Mem, error), copyMem func(pDst, pSrc uint64) error) error { 30 | pSrc, err2 := alloc(size) 31 | if err2 != nil { 32 | return err2 33 | } 34 | defer pSrc.Close() 35 | pDst, err2 := alloc(size) 36 | if err2 != nil { 37 | return err2 38 | } 39 | defer pDst.Close() 40 | dst := pDst.Bytes() 41 | for i := range dst { 42 | dst[i] = 0x11 43 | } 44 | src := make([]byte, size) 45 | for i := range src { 46 | src[i] = byte(rand.Int31()) 47 | } 48 | copy(pSrc.Bytes(), src[:]) 49 | 50 | // Run the driver supplied memory copying code. 51 | if err := copyMem(pDst.PhysAddr(), pSrc.PhysAddr()); err != nil { 52 | return err 53 | } 54 | 55 | // Verifications. 56 | for i := 0; i < holeSize; i++ { 57 | if dst[i] != 0x11 { 58 | return wrapf("DMA corrupted the buffer header: %x", dst[:holeSize]) 59 | } 60 | if dst[size-1-i] != 0x11 { 61 | return wrapf("DMA corrupted the buffer footer: %x", dst[size-1-holeSize:]) 62 | } 63 | } 64 | 65 | // Headers and footers were not corupted in the destination. Verify the inner 66 | // view that should match. 67 | x := src[:size-2*holeSize] 68 | y := dst[holeSize : size-holeSize] 69 | if !bytes.Equal(x, y) { 70 | offset := 0 71 | for len(x) != 0 && x[0] == y[0] { 72 | x = x[1:] 73 | y = y[1:] 74 | offset++ 75 | } 76 | for len(x) != 0 && x[len(x)-1] == y[len(y)-1] { 77 | x = x[:len(x)-1] 78 | y = y[:len(y)-1] 79 | } 80 | if len(x) > 32 { 81 | x = x[:32] 82 | } 83 | if len(y) > 32 { 84 | y = y[:32] 85 | } 86 | return wrapf("DMA corrupted the buffer at offset %d:\n%x\n%x", offset, x, y) 87 | } 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /pmem/smoketest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem 6 | 7 | import ( 8 | "errors" 9 | "reflect" 10 | "testing" 11 | "unsafe" 12 | ) 13 | 14 | func TestSmokeTest_fail(t *testing.T) { 15 | count := 0 16 | alloc := func(size int) (Mem, error) { 17 | if count == 0 { 18 | return nil, errors.New("oops") 19 | } 20 | count-- 21 | return allocRAM(size) 22 | } 23 | 24 | if TestCopy(1024, 1, alloc, copyOk) == nil { 25 | t.Fatal("first alloc failed") 26 | } 27 | count = 1 28 | if TestCopy(1024, 1, alloc, copyOk) == nil { 29 | t.Fatal("second alloc failed") 30 | } 31 | 32 | copyFail := func(d, s uint64) error { 33 | return errors.New("oops") 34 | } 35 | if TestCopy(1024, 1, allocRAM, copyFail) == nil { 36 | t.Fatal("copy failed") 37 | } 38 | 39 | copyNop := func(d, s uint64) error { 40 | return nil 41 | } 42 | if TestCopy(1024, 1, allocRAM, copyNop) == nil { 43 | t.Fatal("no copy") 44 | } 45 | 46 | copyPartial := func(d, s uint64) error { 47 | return copyRAM(d, s, 1024, 2) 48 | } 49 | if TestCopy(1024, 1, allocRAM, copyPartial) == nil { 50 | t.Fatal("copy corrupted") 51 | } 52 | 53 | copyHdr := func(d, s uint64) error { 54 | toSlice(d, int(s))[0] = 0 55 | return nil 56 | } 57 | if TestCopy(1024, 1, allocRAM, copyHdr) == nil { 58 | t.Fatal("header corrupted") 59 | } 60 | 61 | copyFtr := func(d, s uint64) error { 62 | toSlice(d, int(s))[1023] = 0 63 | return copyRAM(d, s, 1024, 1) 64 | } 65 | if TestCopy(1024, 1, allocRAM, copyFtr) == nil { 66 | t.Fatal("footer corrupted") 67 | } 68 | 69 | copyOffset := func(d, s uint64) error { 70 | if err := copyRAM(d, s, 1024, 1); err != nil { 71 | return err 72 | } 73 | toSlice(d, int(s))[3] = 0 74 | return nil 75 | } 76 | if TestCopy(1024, 1, allocRAM, copyOffset) == nil { 77 | t.Fatal("copy corrupted") 78 | } 79 | } 80 | 81 | func TestSmokeTest(t *testing.T) { 82 | // Successfully copy the memory. 83 | if err := TestCopy(1024, 1, allocRAM, copyOk); err != nil { 84 | t.Fatal(err) 85 | } 86 | } 87 | 88 | // allocRAM allocates memory and fake it is physical memory. 89 | func allocRAM(size int) (Mem, error) { 90 | p := make([]byte, size) 91 | return &MemAlloc{ 92 | View: View{ 93 | Slice: Slice(p), 94 | orig: p, 95 | phys: uint64(uintptr(unsafe.Pointer(&p[0]))), 96 | }, 97 | }, nil 98 | } 99 | 100 | func copyOk(d, s uint64) error { 101 | return copyRAM(d, s, 1024, 1) 102 | } 103 | 104 | // copyRAM copies the memory. 105 | func copyRAM(pDst, pSrc uint64, size, hole int) error { 106 | dst := toSlice(pDst, size) 107 | src := toSlice(pSrc, size) 108 | copy(dst[hole:size-hole], src) 109 | return nil 110 | } 111 | 112 | func toSlice(p uint64, size int) []byte { 113 | h := new(reflect.SliceHeader) 114 | h.Data = uintptr(p) 115 | h.Len = size 116 | h.Cap = size 117 | return *(*[]byte)(unsafe.Pointer(h)) 118 | } 119 | -------------------------------------------------------------------------------- /pmem/view_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pmem 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "errors" 11 | "testing" 12 | 13 | "periph.io/x/host/v3/fs" 14 | ) 15 | 16 | func TestSlice(t *testing.T) { 17 | s := Slice([]byte{4, 3, 2, 1}) 18 | if !bytes.Equal([]byte(s), s.Bytes()) { 19 | t.Fatal("Slice.Bytes() is the slice") 20 | } 21 | 22 | // TODO(maruel): Assumes binary.LittleEndian. Correct if this code is ever 23 | // run on BigEndian. 24 | expected := binary.LittleEndian.Uint32(s) 25 | { 26 | v := s.Uint32() 27 | if len(v) != 1 || v[0] != expected { 28 | t.Fatalf("%v", v) 29 | } 30 | var a *[1]uint32 31 | if err := s.AsPOD(&a); err != nil { 32 | t.Fatalf("%v", err) 33 | } 34 | if a[0] != v[0] { 35 | t.Fatalf("%x != %x", a[0], v[0]) 36 | } 37 | } 38 | 39 | { 40 | var v *simpleStruct 41 | if err := s.AsPOD(&v); err != nil { 42 | t.Fatalf("%v", err) 43 | } 44 | if v == nil { 45 | t.Fatal("v is nil") 46 | } 47 | if v.u != expected { 48 | t.Fatalf("%v", v.u) 49 | } 50 | } 51 | 52 | { 53 | var v *uint32 54 | if err := s.AsPOD(&v); err != nil { 55 | t.Fatalf("%v", err) 56 | } 57 | if *v != expected { 58 | t.Fatalf("%v", v) 59 | } 60 | } 61 | 62 | { 63 | var v []uint32 64 | if err := s.AsPOD(&v); err != nil { 65 | t.Fatalf("%v", err) 66 | } 67 | } 68 | } 69 | 70 | func TestSlice_Errors4(t *testing.T) { 71 | s := Slice([]byte{4, 3, 2, 1}) 72 | 73 | if s.AsPOD(nil) == nil { 74 | t.Fatal("nil is not a valid type") 75 | } 76 | 77 | { 78 | var v simpleStruct 79 | if s.AsPOD(v) == nil { 80 | t.Fatal("must be Ptr to Ptr") 81 | } 82 | if s.AsPOD(&v) == nil { 83 | t.Fatal("must be Ptr to Ptr") 84 | } 85 | } 86 | 87 | { 88 | var v *uint32 89 | if s.AsPOD(v) == nil { 90 | t.Fatal("must be Ptr to Ptr") 91 | } 92 | } 93 | 94 | { 95 | var v ***[1]uint32 96 | if s.AsPOD(v) == nil { 97 | t.Fatal("buffer is not large enough") 98 | } 99 | } 100 | 101 | { 102 | var v *int 103 | if s.AsPOD(&v) == nil { 104 | t.Fatal("must be Ptr to sized type") 105 | } 106 | } 107 | 108 | { 109 | v := []uint32{1} 110 | if s.AsPOD(&v) == nil { 111 | t.Fatal("buffer is not large enough") 112 | } 113 | } 114 | 115 | { 116 | var v []interface{} 117 | if s.AsPOD(&v) == nil { 118 | t.Fatal("slice of non-POD") 119 | } 120 | } 121 | 122 | { 123 | var v *struct { 124 | A interface{} 125 | } 126 | if s.AsPOD(&v) == nil { 127 | t.Fatal("struct of non-POD") 128 | } 129 | } 130 | } 131 | 132 | func TestSlice_Errors1(t *testing.T) { 133 | s := Slice([]byte{1}) 134 | { 135 | var v simpleStruct 136 | if s.AsPOD(&v) == nil { 137 | t.Fatal("not large enough") 138 | } 139 | } 140 | 141 | { 142 | var v *[1]uint32 143 | if s.AsPOD(&v) == nil { 144 | t.Fatal("buffer is not large enough") 145 | } 146 | } 147 | 148 | { 149 | var v []uint32 150 | if s.AsPOD(&v) == nil { 151 | t.Fatal("buffer is not large enough") 152 | } 153 | } 154 | } 155 | 156 | // These are really just exercising code, not real tests. 157 | 158 | func TestMapGPIO(t *testing.T) { 159 | defer reset() 160 | // It can fail, depending on the platform. 161 | _, _ = MapGPIO() 162 | } 163 | 164 | func TestMap(t *testing.T) { 165 | defer reset() 166 | if v, err := Map(0, 0); v != nil || err == nil { 167 | t.Fatal("0 size") 168 | } 169 | } 170 | 171 | func TestMapAsPOD(t *testing.T) { 172 | defer reset() 173 | if MapAsPOD(0, nil) == nil { 174 | t.Fatal("0 size") 175 | } 176 | var i *int 177 | if MapAsPOD(0, i) == nil { 178 | t.Fatal("not pointer to pointer") 179 | } 180 | x := 0 181 | i = &x 182 | if MapAsPOD(0, &i) == nil { 183 | t.Fatal("pointer is not nil") 184 | } 185 | 186 | var v *simpleStruct 187 | if MapAsPOD(0, &v) == nil { 188 | t.Fatal("file I/O is inhibited; otherwise it would have worked") 189 | } 190 | } 191 | 192 | func TestView(t *testing.T) { 193 | defer reset() 194 | v := View{} 195 | // Close fails on invalid View. 196 | _ = v.Close() 197 | v.PhysAddr() 198 | } 199 | 200 | // 201 | 202 | type simpleStruct struct { 203 | u uint32 204 | } 205 | 206 | type simpleFile struct { 207 | data []byte 208 | } 209 | 210 | func (s *simpleFile) Close() error { 211 | return nil 212 | } 213 | 214 | func (s *simpleFile) Fd() uintptr { 215 | return 0 216 | } 217 | 218 | func (s *simpleFile) Read(b []byte) (int, error) { 219 | if s.data == nil { 220 | return 0, errors.New("injected") 221 | } 222 | copy(b, s.data) 223 | return len(s.data), nil 224 | } 225 | 226 | func (s *simpleFile) Seek(offset int64, whence int) (int64, error) { 227 | return 0, nil 228 | } 229 | 230 | type failFile struct { 231 | } 232 | 233 | func (f *failFile) Close() error { 234 | return errors.New("injected error") 235 | } 236 | 237 | func (f *failFile) Fd() uintptr { 238 | return 0 239 | } 240 | 241 | func (f *failFile) Read(b []byte) (int, error) { 242 | return 0, errors.New("injected error") 243 | } 244 | 245 | func (f *failFile) Seek(offset int64, whence int) (int64, error) { 246 | return 0, errors.New("injected error") 247 | } 248 | 249 | func reset() { 250 | mu.Lock() 251 | defer mu.Unlock() 252 | gpioMemErr = nil 253 | gpioMemView = nil 254 | devMem = nil 255 | devMemErr = nil 256 | openFile = openFileOrig 257 | pageMap = nil 258 | pageMapErr = nil 259 | } 260 | 261 | func init() { 262 | fs.Inhibit() 263 | } 264 | -------------------------------------------------------------------------------- /pru/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package pru exposes the Programmable Real-Time Unit Subsystem and Industrial 6 | // Communication Subsystem (PRU-ICSS) functionality found on many Texas 7 | // Instruments processors. 8 | // 9 | // This one of the rare way of doing true realtime on a linux microcomputer. 10 | // 11 | // Each PRU is a 32 bits "5ns per instruction" processor running at 200MHz, 12 | // with 8Kb of program memory and 8Kb of data memory. 13 | // 14 | // The PRU processors can be found on 15 | // OMAP-L1x8/C674m/AM18xx/AM335x/AM437x/AM57xx/66AK2Gx families of Texas 16 | // Instrument ARM processors. For example: 17 | // 18 | // - 2x PRUs on am3356, am3357, am3358, am3359 https://www.ti.com/product/am3359 19 | // 20 | // - 4x PRUs on am4376, am4377, am4378, am4379 https://www.ti.com/product/am4379 21 | // 22 | // - 4x PRUs on 66ak2g02; http://www.ti.com/product/66ak2g02 23 | // 24 | // # Datasheet 25 | // 26 | // Technical Reference Manual starting at page 199: 27 | // https://www.ti.com/lit/ug/spruh73p/spruh73p.pdf 28 | // 29 | // List of instructions: 30 | // http://processors.wiki.ti.com/index.php/PRU_Assembly_Instructions 31 | // 32 | // Picture summary of instructions: 33 | // http://exploringbeaglebone.com/wp-content/uploads/2014/12/Instruction-Set-Sheet.png 34 | // 35 | // # Help 36 | // 37 | // Hands-on videos 38 | // http://beagleboard.org/pru 39 | // 40 | // https://elinux.org/Ti_AM33XX_PRUSSv2 41 | // 42 | // http://www.righto.com/2016/09/how-to-run-c-programs-on-beaglebones.html 43 | // 44 | // https://credentiality2.blogspot.ca/2015/09/beaglebone-pru-gpio-example.html 45 | // 46 | // http://processors.wiki.ti.com/index.php/PRU_Training:_Hands-on_Labs 47 | // 48 | // http://processors.wiki.ti.com/index.php/PRU_Linux-based_Example_Code 49 | package pru 50 | -------------------------------------------------------------------------------- /pru/pru.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pru 6 | 7 | import ( 8 | "errors" 9 | 10 | "periph.io/x/conn/v3/driver/driverreg" 11 | ) 12 | 13 | // Present returns true if an Texas Instrument PRU-ICSS processor is detected. 14 | // 15 | // TODO(maruel): Implement. 16 | func Present() bool { 17 | if isArm { 18 | return false 19 | } 20 | return false 21 | } 22 | 23 | // driver implements periph.Driver. 24 | type driver struct { 25 | } 26 | 27 | func (d *driver) String() string { 28 | return "pru" 29 | } 30 | 31 | func (d *driver) Prerequisites() []string { 32 | return nil 33 | } 34 | 35 | func (d *driver) After() []string { 36 | return nil 37 | } 38 | 39 | func (d *driver) Init() (bool, error) { 40 | if !Present() { 41 | return false, errors.New("real time TI's PRU side-CPU not detected") 42 | } 43 | return true, nil 44 | } 45 | 46 | func init() { 47 | if isArm { 48 | driverreg.MustRegister(&drv) 49 | } 50 | } 51 | 52 | var drv driver 53 | -------------------------------------------------------------------------------- /pru/pru_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package pru 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /pru/pru_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm 6 | // +build !arm 7 | 8 | package pru 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /rpi/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package rpi contains Raspberry Pi hardware logic. It is intrinsically 6 | // related to package bcm283x. 7 | // 8 | // Assumes Raspbian but does not directly depend on the distro being Raspbian. 9 | // Windows IoT is currently not supported. 10 | // 11 | // # Physical 12 | // 13 | // The physical pin out is based on http://www.raspberrypi.org information but 14 | // http://pinout.xyz/ has a nice interactive web page. 15 | package rpi 16 | -------------------------------------------------------------------------------- /rpi/rpi_arm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package rpi 6 | 7 | const isArm = true 8 | -------------------------------------------------------------------------------- /rpi/rpi_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build arm64 6 | // +build arm64 7 | 8 | package rpi 9 | 10 | const isArm = true 11 | -------------------------------------------------------------------------------- /rpi/rpi_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !arm && !arm64 6 | // +build !arm,!arm64 7 | 8 | package rpi 9 | 10 | const isArm = false 11 | -------------------------------------------------------------------------------- /serial/serial_fast.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build freebsd || linux || netbsd || solaris 6 | // +build freebsd linux netbsd solaris 7 | 8 | package serial 9 | 10 | import "syscall" 11 | 12 | var acceptedBauds = [][2]uint32{ 13 | {50, syscall.B50}, 14 | {75, syscall.B75}, 15 | {110, syscall.B110}, 16 | {134, syscall.B134}, 17 | {150, syscall.B150}, 18 | {200, syscall.B200}, 19 | {300, syscall.B300}, 20 | {600, syscall.B600}, 21 | {1200, syscall.B1200}, 22 | {1800, syscall.B1800}, 23 | {2400, syscall.B2400}, 24 | {4800, syscall.B4800}, 25 | {9600, syscall.B9600}, 26 | {19200, syscall.B19200}, 27 | {38400, syscall.B38400}, 28 | {57600, syscall.B57600}, 29 | {115200, syscall.B115200}, 30 | {230400, syscall.B230400}, 31 | {460800, syscall.B460800}, 32 | {500000, syscall.B500000}, 33 | {576000, syscall.B576000}, 34 | {921600, syscall.B921600}, 35 | {1000000, syscall.B1000000}, 36 | {1152000, syscall.B1152000}, 37 | {1500000, syscall.B1500000}, 38 | {2000000, syscall.B2000000}, 39 | {2500000, syscall.B2500000}, 40 | {3000000, syscall.B3000000}, 41 | {3500000, syscall.B3500000}, 42 | {4000000, syscall.B4000000}, 43 | } 44 | -------------------------------------------------------------------------------- /serial/serial_not_win.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !windows 6 | // +build !windows 7 | 8 | package serial 9 | 10 | const isWindows = false 11 | -------------------------------------------------------------------------------- /serial/serial_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !freebsd && !linux && !netbsd && !solaris && !darwin && !dragonfly && !openbsd 6 | // +build !freebsd,!linux,!netbsd,!solaris,!darwin,!dragonfly,!openbsd 7 | 8 | package serial 9 | 10 | var acceptedBauds [][2]uint32 11 | -------------------------------------------------------------------------------- /serial/serial_slow.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build darwin || dragonfly || openbsd 6 | // +build darwin dragonfly openbsd 7 | 8 | package serial 9 | 10 | import "syscall" 11 | 12 | var acceptedBauds = [][2]uint32{ 13 | {50, syscall.B50}, 14 | {75, syscall.B75}, 15 | {110, syscall.B110}, 16 | {134, syscall.B134}, 17 | {150, syscall.B150}, 18 | {200, syscall.B200}, 19 | {300, syscall.B300}, 20 | {600, syscall.B600}, 21 | {1200, syscall.B1200}, 22 | {1800, syscall.B1800}, 23 | {2400, syscall.B2400}, 24 | {4800, syscall.B4800}, 25 | {9600, syscall.B9600}, 26 | {19200, syscall.B19200}, 27 | {38400, syscall.B38400}, 28 | {57600, syscall.B57600}, 29 | {115200, syscall.B115200}, 30 | {230400, syscall.B230400}, 31 | } 32 | -------------------------------------------------------------------------------- /serial/serial_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build windows 6 | // +build windows 7 | 8 | package serial 9 | 10 | const isWindows = true 11 | -------------------------------------------------------------------------------- /sysfs/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package sysfs implements a sane library to interact with sysfs provided 6 | // hardware access. 7 | // 8 | // sysfs a virtual file system rooted at /sys/. 9 | // 10 | // This package also include drivers using devfs. 11 | // 12 | // https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt 13 | package sysfs 14 | -------------------------------------------------------------------------------- /sysfs/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfs_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "periph.io/x/conn/v3/gpio" 12 | "periph.io/x/host/v3" 13 | "periph.io/x/host/v3/sysfs" 14 | ) 15 | 16 | func ExampleLEDByName() { 17 | // Make sure periph is initialized. 18 | if _, err := host.Init(); err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | for _, led := range sysfs.LEDs { 23 | fmt.Printf("- %s: %s\n", led, led.Func()) 24 | } 25 | led, err := sysfs.LEDByName("LED0") 26 | if err != nil { 27 | log.Fatalf("failed to find LED: %v", err) 28 | } 29 | if err := led.Out(gpio.Low); err != nil { 30 | log.Fatal(err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sysfs/fs_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !linux 6 | // +build !linux 7 | 8 | package sysfs 9 | 10 | type eventsListener struct { 11 | } 12 | 13 | // events is the global events listener. 14 | // 15 | // It is not used outside linux. 16 | var events eventsListener 17 | -------------------------------------------------------------------------------- /sysfs/i2c_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfs 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/conn/v3/i2c/i2creg" 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | func TestNewI2C(t *testing.T) { 15 | if b, err := NewI2C(-1); b != nil || err == nil { 16 | t.Fatal("invalid bus") 17 | } 18 | } 19 | 20 | func TestI2C_faked(t *testing.T) { 21 | // Create a fake I2C to test methods. 22 | bus := I2C{f: &ioctlClose{}, busNumber: 24} 23 | if s := bus.String(); s != "I2C24" { 24 | t.Fatal(s) 25 | } 26 | if bus.Tx(0x401, nil, nil) == nil { 27 | t.Fatal("empty Tx") 28 | } 29 | if err := bus.Tx(1, nil, nil); err != nil { 30 | t.Fatal(err) 31 | } 32 | if err := bus.Tx(1, []byte{0}, nil); err != nil { 33 | t.Fatal(err) 34 | } 35 | if err := bus.Tx(1, nil, []byte{0}); err != nil { 36 | t.Fatal(err) 37 | } 38 | if err := bus.Tx(1, []byte{0}, []byte{0}); err != nil { 39 | t.Fatal(err) 40 | } 41 | if bus.SetSpeed(0) == nil { 42 | t.Fatal("0 is invalid") 43 | } 44 | if bus.SetSpeed(physic.Hertz) == nil { 45 | t.Fatal("can't set speed") 46 | } 47 | bus.SCL() 48 | bus.SDA() 49 | if err := bus.Close(); err != nil { 50 | t.Fatal(err) 51 | } 52 | } 53 | 54 | func TestI2C_functionality(t *testing.T) { 55 | expected := "I2C|10BIT_ADDR|PROTOCOL_MANGLING|SMBUS_PEC|NOSTART|SMBUS_BLOCK_PROC_CALL|SMBUS_QUICK|SMBUS_READ_BYTE|SMBUS_WRITE_BYTE|SMBUS_READ_BYTE_DATA|SMBUS_WRITE_BYTE_DATA|SMBUS_READ_WORD_DATA|SMBUS_WRITE_WORD_DATA|SMBUS_PROC_CALL|SMBUS_READ_BLOCK_DATA|SMBUS_WRITE_BLOCK_DATA|SMBUS_READ_I2C_BLOCK|SMBUS_WRITE_I2C_BLOCK" 56 | if s := functionality(0xFFFFFFFF).String(); s != expected { 57 | t.Fatal(s) 58 | } 59 | } 60 | 61 | func TestDriver_Init(t *testing.T) { 62 | d := driverI2C{} 63 | if _, err := d.Init(); err == nil { 64 | // It will fail on non-linux. 65 | defer func() { 66 | for _, name := range d.buses { 67 | if err := i2creg.Unregister(name); err != nil { 68 | t.Fatal(err) 69 | } 70 | } 71 | }() 72 | if len(d.buses) != 0 { 73 | // It may fail due to ACL. 74 | b, _ := i2creg.Open("") 75 | if b != nil { 76 | // If opening succeeded, closing must always succeed. 77 | if err := b.Close(); err != nil { 78 | t.Fatal(err) 79 | } 80 | } 81 | } 82 | } 83 | if d.Prerequisites() != nil { 84 | t.Fatal("unexpected prerequisite") 85 | } 86 | if drvI2C.setSpeed != nil { 87 | t.Fatal("unexpected setSpeed") 88 | } 89 | defer func() { 90 | drvI2C.setSpeed = nil 91 | }() 92 | if I2CSetSpeedHook(nil) == nil { 93 | t.Fatal("must fail on nil hook") 94 | } 95 | if err := I2CSetSpeedHook(func(f physic.Frequency) error { return nil }); err != nil { 96 | t.Fatal(err) 97 | } 98 | if I2CSetSpeedHook(func(f physic.Frequency) error { return nil }) == nil { 99 | t.Fatal("second I2CSetSpeedHook must fail") 100 | } 101 | } 102 | 103 | func BenchmarkI2C(b *testing.B) { 104 | b.ReportAllocs() 105 | i := ioctlClose{} 106 | bus := I2C{f: &i} 107 | var w [16]byte 108 | var r [16]byte 109 | for i := 0; i < b.N; i++ { 110 | if err := bus.Tx(0x01, w[:], r[:]); err != nil { 111 | b.Fatal(err) 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /sysfs/led_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfs 6 | 7 | import ( 8 | "testing" 9 | 10 | "periph.io/x/conn/v3/gpio" 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | func TestLEDByName(t *testing.T) { 15 | if _, err := LEDByName("FOO"); err == nil { 16 | t.Fatal("expected error") 17 | } 18 | } 19 | 20 | func TestLED(t *testing.T) { 21 | l := LED{number: 42, name: "Glow", root: "/tmp/led/priv/"} 22 | if s := l.String(); s != "Glow(42)" { 23 | t.Fatal(s) 24 | } 25 | if s := l.Name(); s != "Glow" { 26 | t.Fatal(s) 27 | } 28 | if n := l.Number(); n != 42 { 29 | t.Fatal(n) 30 | } 31 | } 32 | 33 | func TestLEDMock(t *testing.T) { 34 | l := LED{number: 42, name: "Glow", root: "/tmp/led/priv/"} 35 | if s := l.Func(); s != "LED/Off" { 36 | t.Fatal(s) 37 | } 38 | if err := l.In(gpio.PullNoChange, gpio.NoEdge); err != nil { 39 | t.Fatal(err) 40 | } 41 | if l := l.Read(); l != gpio.Low { 42 | t.Fatal("need mock") 43 | } 44 | if err := l.Out(gpio.High); err == nil { 45 | t.Fatal("need mock") 46 | } 47 | } 48 | 49 | func TestLED_not_supported(t *testing.T) { 50 | l := LED{number: 42, name: "Glow", root: "/tmp/led/priv/"} 51 | if err := l.In(gpio.PullDown, gpio.NoEdge); err == nil { 52 | t.Fatal("sysfs-led no real In() support") 53 | } 54 | if l.WaitForEdge(-1) { 55 | t.Fatal("not supported") 56 | } 57 | if pull := l.Pull(); pull != gpio.PullNoChange { 58 | t.Fatal(pull) 59 | } 60 | if l.PWM(gpio.DutyHalf, physic.KiloHertz) == nil { 61 | t.Fatal("not supported") 62 | } 63 | } 64 | 65 | func TestLEDDriver(t *testing.T) { 66 | if len((&driverLED{}).Prerequisites()) != 0 { 67 | t.Fatal("unexpected LED prerequisites") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sysfs/sysfs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfs 6 | 7 | import ( 8 | "io" 9 | 10 | "periph.io/x/host/v3/fs" 11 | ) 12 | 13 | var ioctlOpen = ioctlOpenDefault 14 | 15 | func ioctlOpenDefault(path string, flag int) (ioctlCloser, error) { 16 | f, err := fs.Open(path, flag) 17 | if err != nil { 18 | return nil, err 19 | } 20 | return f, nil 21 | } 22 | 23 | var fileIOOpen = fileIOOpenDefault 24 | 25 | func fileIOOpenDefault(path string, flag int) (fileIO, error) { 26 | f, err := fs.Open(path, flag) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return f, nil 31 | } 32 | 33 | type ioctlCloser interface { 34 | io.Closer 35 | fs.Ioctler 36 | } 37 | 38 | type fileIO interface { 39 | Fd() uintptr 40 | fs.Ioctler 41 | io.Closer 42 | io.Reader 43 | io.Seeker 44 | io.Writer 45 | } 46 | 47 | // seekRead seeks to the beginning of a file and reads it. 48 | func seekRead(f fileIO, b []byte) (int, error) { 49 | if _, err := f.Seek(0, 0); err != nil { 50 | return 0, err 51 | } 52 | return f.Read(b) 53 | } 54 | 55 | // seekWrite seeks to the beginning of a file and writes to it. 56 | func seekWrite(f fileIO, b []byte) error { 57 | if _, err := f.Seek(0, 0); err != nil { 58 | return err 59 | } 60 | _, err := f.Write(b) 61 | return err 62 | } 63 | -------------------------------------------------------------------------------- /sysfs/sysfs_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfs 6 | 7 | import ( 8 | "os" 9 | "syscall" 10 | ) 11 | 12 | const isLinux = true 13 | 14 | func isErrBusy(err error) bool { 15 | e, ok := err.(*os.PathError) 16 | return ok && e.Err == syscall.EBUSY 17 | } 18 | -------------------------------------------------------------------------------- /sysfs/sysfs_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | //go:build !linux 6 | // +build !linux 7 | 8 | package sysfs 9 | 10 | const isLinux = false 11 | 12 | func isErrBusy(err error) bool { 13 | // This function is not used on non-linux. 14 | return false 15 | } 16 | -------------------------------------------------------------------------------- /sysfs/sysfs_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfs 6 | 7 | import ( 8 | "errors" 9 | "io" 10 | 11 | "periph.io/x/host/v3/fs" 12 | ) 13 | 14 | func init() { 15 | fs.Inhibit() 16 | reset() 17 | } 18 | 19 | func reset() { 20 | fileIOOpen = fileIOOpenDefault 21 | ioctlOpen = ioctlOpenDefault 22 | // Soon. 23 | //fileIOOpen = fileIOOpenPanic 24 | //ioctlOpen = ioctlOpenPanic 25 | } 26 | 27 | func ioctlOpenPanic(path string, flag int) (ioctlCloser, error) { 28 | panic("don't forget to override fileIOOpen") 29 | } 30 | 31 | func fileIOOpenPanic(path string, flag int) (fileIO, error) { 32 | panic("don't forget to override fileIOOpen") 33 | } 34 | 35 | type ioctlClose struct { 36 | ioctlErr error 37 | closeErr error 38 | } 39 | 40 | func (i *ioctlClose) Ioctl(op uint, data uintptr) error { 41 | return i.ioctlErr 42 | } 43 | 44 | func (i *ioctlClose) Close() error { 45 | return i.closeErr 46 | } 47 | 48 | type file struct { 49 | ioctlClose 50 | } 51 | 52 | func (f *file) Fd() uintptr { 53 | return 0xFFFFFFFF 54 | } 55 | 56 | func (f *file) Read(p []byte) (int, error) { 57 | return 0, errors.New("not implemented") 58 | } 59 | 60 | func (f *file) Seek(offset int64, whence int) (int64, error) { 61 | if offset == 0 && whence == io.SeekStart { 62 | return 0, nil 63 | } 64 | return 0, errors.New("not implemented") 65 | } 66 | 67 | func (f *file) Write(p []byte) (int, error) { 68 | return 0, errors.New("not implemented") 69 | } 70 | -------------------------------------------------------------------------------- /sysfs/sysfssmoketest/benchmark.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfssmoketest 6 | 7 | import ( 8 | "errors" 9 | "flag" 10 | "fmt" 11 | "sort" 12 | 13 | "periph.io/x/conn/v3/gpio" 14 | "periph.io/x/host/v3/sysfs" 15 | ) 16 | 17 | // Benchmark is imported by periph-smoketest. 18 | type Benchmark struct { 19 | short bool 20 | p *sysfs.Pin 21 | pull gpio.Pull 22 | } 23 | 24 | // Name implements the SmokeTest interface. 25 | func (s *Benchmark) Name() string { 26 | return "sysfs-benchmark" 27 | } 28 | 29 | // Description implements the SmokeTest interface. 30 | func (s *Benchmark) Description() string { 31 | return "Benchmarks sysfs gpio functionality" 32 | } 33 | 34 | // Run implements the SmokeTest interface. 35 | func (s *Benchmark) Run(f *flag.FlagSet, args []string) error { 36 | num := f.Int("p", -1, "Pin number to use") 37 | f.BoolVar(&s.short, "short", false, "Skip many partially redundant benchmarks") 38 | if err := f.Parse(args); err != nil { 39 | return err 40 | } 41 | 42 | if f.NArg() != 0 { 43 | f.Usage() 44 | return errors.New("unsupported flags") 45 | } 46 | if *num == -1 { 47 | f.Usage() 48 | return errors.New("-p is required") 49 | } 50 | if s.p = sysfs.Pins[*num]; s.p == nil { 51 | list := make([]int, 0, len(sysfs.Pins)) 52 | for i := range sysfs.Pins { 53 | list = append(list, i) 54 | } 55 | sort.Ints(list) 56 | valid := "" 57 | for i, v := range list { 58 | if i == 0 { 59 | valid += fmt.Sprintf("%d", v) 60 | } else { 61 | valid += fmt.Sprintf(", %d", v) 62 | } 63 | } 64 | return fmt.Errorf("invalid pin %d; valid: %s", *num, valid) 65 | } 66 | s.pull = gpio.PullNoChange 67 | s.runGPIOBenchmark() 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /sysfs/sysfssmoketest/benchmark_gpio_support_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package sysfssmoketest 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "periph.io/x/conn/v3/physic" 12 | ) 13 | 14 | func TestToHz(t *testing.T) { 15 | data := []struct { 16 | N int 17 | T time.Duration 18 | expected physic.Frequency 19 | }{ 20 | { 21 | 0, 22 | time.Second, 23 | 0, 24 | }, 25 | { 26 | 1, 27 | 0, 28 | 0, 29 | }, 30 | { 31 | 1, 32 | time.Millisecond, 33 | physic.KiloHertz, 34 | }, 35 | { 36 | 1, 37 | time.Second, 38 | physic.Hertz, 39 | }, 40 | { 41 | 3, 42 | 7 * time.Millisecond, 43 | // 3/7 with perfect rounding. 44 | 428571429 * physic.MicroHertz, 45 | }, 46 | { 47 | 3000, 48 | 7 * time.Microsecond, 49 | // 3/7 with perfect rounding. 50 | 428571428571429 * physic.MicroHertz, 51 | }, 52 | { 53 | 1000000, 54 | 1000 * time.Second, 55 | physic.KiloHertz, 56 | }, 57 | { 58 | 1000000, 59 | time.Second, 60 | physic.MegaHertz, 61 | }, 62 | { 63 | 1000000, 64 | time.Millisecond, 65 | physic.GigaHertz, 66 | }, 67 | { 68 | 1000000000, 69 | 1000 * time.Second, 70 | physic.MegaHertz, 71 | }, 72 | { 73 | 1000000000, 74 | time.Second, 75 | physic.GigaHertz, 76 | }, 77 | { 78 | 1234556000, 79 | // 2.3s. 80 | 2345567891 * time.Nanosecond, 81 | // 10 digits of resolution for 526.336MHz. 82 | 526335849711 * physic.MilliHertz, 83 | }, 84 | { 85 | 1000000000, 86 | time.Millisecond, 87 | physic.TeraHertz, 88 | }, 89 | { 90 | 300000000, 91 | 7 * time.Millisecond, 92 | // 3/7 with pretty good rounding, keeping in mind that's 42.857GHz. 93 | 42857142857143 * physic.MilliHertz, 94 | }, 95 | } 96 | for i, line := range data { 97 | r := testing.BenchmarkResult{N: line.N, T: line.T} 98 | if actual := toHz(&r); actual != line.expected { 99 | t.Fatalf("#%d: toHz(%d, %s) = %s(%d); expected %s(%d)", i, line.N, line.T, actual, actual, line.expected, line.expected) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /sysfs/sysfssmoketest/sysfssmoketest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | // Package sysfssmoketest verifies that sysfs specific functionality work. 6 | package sysfssmoketest 7 | -------------------------------------------------------------------------------- /videocore/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package videocore_test 6 | 7 | import ( 8 | "log" 9 | 10 | "periph.io/x/host/v3/videocore" 11 | ) 12 | 13 | func ExampleAlloc() { 14 | // Allocates physical memory on a Broadcom CPU by leveraging the GPU. 15 | // This memory can be leveraged to do DMA operations. 16 | m, err := videocore.Alloc(64536) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | // Use m 21 | if err := m.Close(); err != nil { 22 | log.Fatal(err) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /videocore/videocore_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Periph Authors. All rights reserved. 2 | // Use of this source code is governed under the Apache License, Version 2.0 3 | // that can be found in the LICENSE file. 4 | 5 | package videocore 6 | 7 | import ( 8 | "errors" 9 | "testing" 10 | 11 | "periph.io/x/host/v3/fs" 12 | "periph.io/x/host/v3/pmem" 13 | ) 14 | 15 | func TestClose(t *testing.T) { 16 | defer reset(t) 17 | mailbox = &dummy{} 18 | m := Mem{View: &pmem.View{}} 19 | if m.Close() == nil { 20 | t.Fatal("can't close uninitialized pmem.View") 21 | } 22 | } 23 | 24 | func TestAlloc_fail(t *testing.T) { 25 | defer reset(t) 26 | if m, err := Alloc(0); m != nil || err == nil { 27 | t.Fatal("can't alloc 0 bytes") 28 | } 29 | if m, err := Alloc(1); m != nil || err == nil { 30 | t.Fatal("can't alloc non 4096 bytes increments") 31 | } 32 | mailboxErr = errors.New("error") 33 | if m, err := Alloc(4096); m != nil || err == nil { 34 | t.Fatal("mailboxErr is not nil") 35 | } 36 | mailboxErr = nil 37 | mailbox = &dummy{} 38 | if m, err := Alloc(4096); m != nil || err == nil { 39 | t.Fatal("can't map arbitrary physical pages") 40 | } 41 | mailbox = &playback{} 42 | if m, err := Alloc(4096); m != nil || err == nil { 43 | t.Fatal("mailbox failed") 44 | } 45 | mailbox = &playback{ 46 | reply: []uint32{0}, 47 | } 48 | if m, err := Alloc(4096); m != nil || err == nil { 49 | t.Fatal("mailbox failed") 50 | } 51 | mailbox = &playback{ 52 | reply: []uint32{failReply}, 53 | } 54 | if m, err := Alloc(4096); m != nil || err == nil { 55 | t.Fatal("mailbox failed") 56 | } 57 | mailbox = &playback{ 58 | reply: []uint32{failReplyLen}, 59 | } 60 | if m, err := Alloc(4096); m != nil || err == nil { 61 | t.Fatal("mailbox failed") 62 | } 63 | mailbox = &playback{ 64 | reply: []uint32{1, 0}, 65 | } 66 | if m, err := Alloc(4096); m != nil || err == nil { 67 | t.Fatal("mailbox failed") 68 | } 69 | mailbox = &playback{ 70 | reply: []uint32{1, failReply}, 71 | } 72 | if m, err := Alloc(4096); m != nil || err == nil { 73 | t.Fatal("mailbox failed") 74 | } 75 | } 76 | 77 | func TestOpenMailbox(t *testing.T) { 78 | defer reset(t) 79 | mailbox = &playback{} 80 | if err := openMailbox(); err != nil { 81 | if mailboxErr != err { 82 | t.Fatal("error is different") 83 | } 84 | if mailbox != nil { 85 | t.Fatal("mailbox should be nil") 86 | } 87 | } else { 88 | if mailboxErr != nil { 89 | t.Fatal("should have error'ed") 90 | } 91 | if mailbox == nil { 92 | t.Fatal("mailbox should not be nil") 93 | } 94 | } 95 | // No-op in any case. 96 | if err := openMailbox(); err != nil { 97 | t.Fatal(err) 98 | } 99 | } 100 | 101 | func TestSmokeTest(t *testing.T) { 102 | defer reset(t) 103 | mailbox = &dummy{} 104 | if err := smokeTest(); err != nil { 105 | t.Fatal(err) 106 | } 107 | } 108 | 109 | func TestGenPacket(t *testing.T) { 110 | defer reset(t) 111 | actual := genPacket(10, 12, 1, 2, 3) 112 | expected := []uint32{0x24, 0x0, 0xa, 0xc, 0xc, 0x1, 0x2, 0x3, 0x0} 113 | if !uint32Equals(actual, expected) { 114 | t.Fatal(actual) 115 | } 116 | actual = genPacket(10, 12, 1, 2) 117 | expected = []uint32{0x24, 0x0, 0xa, 0x8, 0xc, 0x1, 0x2, 0x0, 0x0} 118 | if !uint32Equals(actual, expected) { 119 | t.Fatal(actual) 120 | } 121 | } 122 | 123 | // 124 | 125 | type dummy struct{} 126 | 127 | func (d *dummy) sendMessage(b []uint32) error { 128 | b[1] = mbReply 129 | b[4] = mbReply | 4 130 | return nil 131 | } 132 | 133 | type playback struct { 134 | reply []uint32 135 | count int 136 | } 137 | 138 | const failReply uint32 = 0xFFFFFFFE 139 | const failReplyLen uint32 = 0xFFFFFFFF 140 | 141 | func (p *playback) sendMessage(b []uint32) error { 142 | if len(p.reply) <= p.count { 143 | return errors.New("exceeded count") 144 | } 145 | b[5] = p.reply[p.count] 146 | if b[5] != failReply { 147 | b[1] = mbReply 148 | } 149 | if b[5] != failReplyLen { 150 | b[4] = mbReply | 4 151 | } 152 | p.count++ 153 | return nil 154 | } 155 | 156 | func uint32Equals(a []uint32, b []uint32) bool { 157 | if len(a) != len(b) { 158 | return false 159 | } 160 | for i, v := range a { 161 | if v != b[i] { 162 | return false 163 | } 164 | } 165 | return true 166 | } 167 | 168 | func reset(t *testing.T) { 169 | mu.Lock() 170 | defer mu.Unlock() 171 | if mailbox != nil { 172 | if m, ok := mailbox.(*messageBox); ok { 173 | if err := m.f.Close(); err != nil { 174 | t.Fatal(err) 175 | } 176 | } 177 | } 178 | mailbox = nil 179 | mailboxErr = nil 180 | } 181 | 182 | func init() { 183 | fs.Inhibit() 184 | } 185 | --------------------------------------------------------------------------------