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