github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/gpio/gpio_linux.go (about) 1 // Copyright 2019 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package gpio provides functions for interacting with GPIO pins via the 6 // GPIO Sysfs Interface for Userspace. 7 package gpio 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "strconv" 15 "strings" 16 ) 17 18 const gpioPath = "/sys/class/gpio" 19 20 // Value represents the value of a gpio pin 21 type Value bool 22 23 // Gpio pin values can either be low (0) or high (1) 24 const ( 25 Low Value = false 26 High Value = true 27 ) 28 29 // Dir returns the representation that sysfs likes to use. 30 func (v Value) Dir() string { 31 if v == Low { 32 return "low" 33 } 34 return "high" 35 } 36 37 func (v Value) String() string { 38 if v == Low { 39 return "0" 40 } 41 return "1" 42 } 43 44 func readInt(filename string) (int, error) { 45 // Get base offset (the first GPIO managed by this chip) 46 buf, err := ioutil.ReadFile(filename) 47 if err != nil { 48 return 0, fmt.Errorf("failed to read integer out of %s: %v", filename, err) 49 } 50 baseStr := strings.TrimSpace(string(buf)) 51 num, err := strconv.Atoi(baseStr) 52 if err != nil { 53 return 0, fmt.Errorf("could not convert %s contents %s to integer: %v", filename, baseStr, err) 54 } 55 return num, nil 56 } 57 58 // GetPinID computes the sysfs pin ID for a specific port on a specific GPIO 59 // controller chip. The controller arg is matched to a gpiochip's label in 60 // sysfs. GetPinID gets the base offset of that chip, and adds the specific 61 // pin number. 62 func GetPinID(controller string, pin uint) (int, error) { 63 controllers, err := filepath.Glob(fmt.Sprintf("%s/gpiochip*", gpioPath)) 64 if err != nil { 65 return 0, err 66 } 67 68 for _, c := range controllers { 69 // Get label (name of the controller) 70 buf, err := ioutil.ReadFile(filepath.Join(c, "label")) 71 if err != nil { 72 return 0, fmt.Errorf("failed to read label of %s: %v", c, err) 73 } 74 label := strings.TrimSpace(string(buf)) 75 76 // Check that this is the controller we want 77 if strings.TrimSpace(label) != controller { 78 continue 79 } 80 81 // Get base offset (the first GPIO managed by this chip) 82 base, err := readInt(filepath.Join(c, "base")) 83 if err != nil { 84 return 0, fmt.Errorf("failed to read base: %v", err) 85 } 86 87 // Get the number of GPIOs managed by this chip. 88 ngpio, err := readInt(filepath.Join(c, "ngpio")) 89 if err != nil { 90 return 0, fmt.Errorf("failed to read number of gpios: %v", err) 91 } 92 if int(pin) >= ngpio { 93 return 0, fmt.Errorf("requested pin %d of controller %s, but controller only has %d pins", pin, controller, ngpio) 94 } 95 96 return base + int(pin), nil 97 } 98 99 return 0, fmt.Errorf("could not find controller %s", controller) 100 } 101 102 // SetOutputValue configures the gpio as an output pin with the given value. 103 func SetOutputValue(pin int, val Value) error { 104 dir := val.Dir() 105 path := filepath.Join(gpioPath, fmt.Sprintf("gpio%d", pin), "direction") 106 outFile, err := os.OpenFile(path, os.O_WRONLY, 0) 107 if err != nil { 108 return fmt.Errorf("failed to open %s: %v", path, err) 109 } 110 defer outFile.Close() 111 if _, err := outFile.WriteString(dir); err != nil { 112 return fmt.Errorf("failed to set gpio %d to %s: %v", pin, dir, err) 113 } 114 return nil 115 } 116 117 // ReadValue returns the value of the given gpio pin. If the read was 118 // unsuccessful, it returns a value of Low and the associated error. 119 func ReadValue(pin int) (Value, error) { 120 path := filepath.Join(gpioPath, fmt.Sprintf("gpio%d", pin), "value") 121 buf, err := ioutil.ReadFile(path) 122 if err != nil { 123 return Low, fmt.Errorf("failed to read value of gpio %d: %v", pin, err) 124 } 125 switch string(buf) { 126 case "0\n": 127 return Low, nil 128 case "1\n": 129 return High, nil 130 } 131 return Low, fmt.Errorf("invalid value of gpio %d: %s", pin, string(buf)) 132 } 133 134 // Export enables access to the given gpio pin. 135 func Export(pin int) error { 136 path := filepath.Join(gpioPath, "export") 137 outFile, err := os.OpenFile(path, os.O_WRONLY, 0) 138 if err != nil { 139 return fmt.Errorf("failed to open %s: %v", path, err) 140 } 141 defer outFile.Close() 142 if _, err := outFile.WriteString(strconv.Itoa(pin)); err != nil { 143 return fmt.Errorf("failed to export gpio %d: %v", pin, err) 144 } 145 return nil 146 }