A nova interface de GPIO do kernel Linux

- por Sergio Prado

Categorias: Linux Tags: ,

Desde a versão 4.8 do kernel existe uma nova interface baseada em dispositivos de caractere para manipular e gerenciar linhas de GPIO em espaço de usuário no Linux.

Interface de GPIO do kernel Linux

GPIO significa General-Purpose Input/Output e é um dos mais comuns periféricos de propósito geral utilizados em um sistema com Linux embarcado.

Internamente, o kernel Linux implementa o acesso a linhas de GPIO utilizando um modelo produtor/consumidor. Existem drivers que produzem GPIOs (ex: controladoras de GPIO, expansoras de GPIO, etc) e drivers que consomem GPIOs (ex: teclado, touchscreen, sensores, etc).

Para gerenciar o registro e a alocação de GPIOs existe um framework no kernel chamado gpiolib. Este framework provê uma API de acesso aos GPIOs tanto para drivers de dispositivo rodando em espaço de kernel quanto para aplicações e bibliotecas rodando em espaço de usuário.

A (antiga) interface via sysfs

Até a versão 4.7 do kernel Linux, a interface de acesso a GPIOs em espaço de usuário é no sysfs por meio de arquivos exportados em /sys/class/gpio/.

Por exemplo, para ligar um GPIO via sysfs é necessário:

  1. Identificar o número do GPIO que deseja-se ligar (todo GPIO tem um número sequencial associado a ele no kernel).
  2. Exportar o GPIO escrevendo seu número no arquivo /sys/class/gpio/export.
  3. Configurar a linha de GPIO como saída escrevendo out em /sys/class/gpio/gpioX/direction.
  4. Ligar o GPIO escrevendo 1 em /sys/class/gpio/gpioX/value.

Estes então seriam os comandos para ligar o GPIO 504 via sysfs:

# echo 504 > /sys/class/gpio/export
# echo out > /sys/class/gpio/gpio504/direction 
# echo 1   > /sys/class/gpio/gpio504/value

É uma interface simples e funcional, mas com algumas deficiências:

  • A alocação do GPIO não está vinculada a nenhum processo. Portanto, se por exemplo o processo que está usando o GPIO encerrar sua execução abruptamente, a linha de GPIO poderá permanecer exportada.
  • Vários processos podem acessar um GPIO ao mesmo tempo, causando problemas de concorrência.
  • Escrever em vários pinos de I/O exigiria diversas chamadas de open(), read(), write() e close() em vários arquivos do sysfs (export, direction, value, etc).
  • O processo de polling para capturar eventos (interrupções das linhas de GPIO) não é confiável.
  • Não há uma interface para configurar a linha de GPIO (open-source, open-drain, etc).
  • Os números dos GPIOs não são estáveis e podem mudar dependendo da configuração do hardware.

A (nova) interface via char device

A partir da versão 4.8 do kernel, a interface via sysfs ficou obsoleta. Agora temos uma nova API baseada em arquivos de dispositivo de caractere (char device) para acessar as linhas de GPIO em espaço de usuário no Linux.

Cada controladora de GPIO (gpiochip) tem um arquivo de dispositivo de caractere exportado no /dev, possibilitando o acesso aos GPIOs através de operações em arquivo (open(), read(), write(), ioctl(), poll(), close()):

# ls /dev/gpiochip*
/dev/gpiochip0  /dev/gpiochip2  /dev/gpiochip4  /dev/gpiochip6
/dev/gpiochip1  /dev/gpiochip3  /dev/gpiochip5  /dev/gpiochip7

Embora esta nova interface impeça a manipulação de GPIOs diretamente do terminal utilizando ferramentas como echo e cat, ela possui algumas vantagens quando comparada à interface via sysfs:

  • A alocação do GPIO estará associada ao processo que está usando o GPIO, melhorando o controle sobre quais linhas de GPIO estão sendo utilizadas pelos processos do sistema e possibilitando desalocar o GPIO automaticamente quando o processo encerrar sua execução.
  • É possível ler ou gravar em várias linhas de GPIO de uma só vez.
  • A interface permite configurar o estado de um pino de I/O (open-source, open-drain, etc).
  • É possível fazer buscas de controladoras e linhas de GPIO por nome.
  • O processo de polling para capturar eventos (interrupções das linhas de GPIO) é confiável.

A biblioteca libgpiod

Para facilitar o uso desta nova interface de GPIO existe uma biblioteca e um conjunto de ferramentas providas pelo projeto libgpiod.

Por exemplo, o programa em C abaixo utiliza a libgpiod para ler o estado de um GPIO:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void main() {
	struct gpiod_chip *chip;
	struct gpiod_line *line;
	int req, value;
 
	chip = gpiod_chip_open("/dev/gpiochip0");
	if (!chip)
		return -1;
 
	line = gpiod_chip_get_line(chip, 3);
	if (!line) {
		gpiod_chip_close(chip);
		return -1;
	}
 
	req = gpiod_line_request_input(line, "gpio_state");
	if (req) {
		gpiod_chip_close(chip);
		return -1;
	}
 
	value = gpiod_line_get_value(line);
 
	printf("GPIO value is: %d\n", value);
 
	gpiod_chip_close(chip);
}

A biblioteca libgpiod foi desenvolvida em C, mas existem bindings para outras linguagens como C++ e Python.

Ferramentas de linha de comando

Para interagir esta interface de GPIO em um terminal, podemos utilizar as diversas ferramentas de linha de comando providas pela biblioteca libgpiod.

Por exemplo, o comando gpiodetect irá listar informações sobre todas as controladoras de GPIO, incluindo nome e quantidade de linhas de GPIO gerenciadas por cada controladora:

# gpiodetect
gpiochip0 [209c000.gpio] (32 lines)
gpiochip1 [20a0000.gpio] (32 lines)
gpiochip2 [20a4000.gpio] (32 lines)
gpiochip3 [20a8000.gpio] (32 lines)
gpiochip4 [20ac000.gpio] (32 lines)
gpiochip5 [20b0000.gpio] (32 lines)
gpiochip6 [20b4000.gpio] (32 lines)
gpiochip7 [mcp23s08.0] (8 lines)

Já o comando gpioinfo irá exibir informações sobre as linhas de GPIO de uma controladora específica (ou de todas as controladoras se nenhuma for especificada):

# gpioinfo 0
gpiochip0 - 32 lines:
        line   0:      unnamed       unused   input  active-high 
        line   1:      unnamed       unused   input  active-high 
        line   2:      unnamed       unused   input  active-high 
        line   3:      unnamed        "scl"  output  active-high [used open-drain]
        line   4:      unnamed       unused   input  active-high 
        line   5:      unnamed       unused   input  active-high 
        line   6:      unnamed        "sda"  output  active-high [used open-drain]
        line   7:      unnamed "Push Button"  input  active-low  [used]
        line   8:      unnamed       unused   input  active-high 
        line   9:      unnamed          "?"  output  active-high [used]
        line  10:      unnamed       unused   input  active-high 
        line  11:      unnamed       unused   input  active-high
[...]

O comando gpioset é capaz de ligar um GPIO. Por exemplo, o comando abaixo irá ligar o GPIO 0 da controladora 7:

# gpioset 7 0=1

Da mesma forma, o comando gpioget é capaz de ler o estado de um GPIO. Por exemplo, o comando abaixo irá ler o estado do GPIO 0 da controladora 7:

# gpioget 7 0
1

Por fim, o comando gpiomon irá esperar por eventos em linhas de GPIO e informar quando estes eventos acontecerem:

# gpiomon 0 7
event: FALLING EDGE offset: 7 timestamp: [    4564.943435210]
event:  RISING EDGE offset: 7 timestamp: [    4565.072156210]
event: FALLING EDGE offset: 7 timestamp: [    4566.113641877]
event:  RISING EDGE offset: 7 timestamp: [    4566.366691877]

O código-fonte de todos esses comandos está disponível no repositório da libgpiod.

Esta nova interface para manipular GPIOs possui uma API muito simples, elegante e robusta, e deve ser utilizada de agora em diante no desenvolvimento de sistemas com Linux embarcado.

Um abraço,

Sergio Prado

Sem Comentários

Nenhum comentário até agora... é a sua chance de ser o primeiro a comentar!

Faça um Comentário

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.