github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/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  }