11 Comentários

Device Drivers para Linux Embarcado – Introdução

device driver

Neste artigo eu irei falar sobre desenvolvimento de device drivers para Linux embarcado. Há certa carência de documentação ou tutoriais sobre o assunto. Espero com este artigo preencher um pouco essa lacuna.

Introdução

Aplicações em espaço de usúario não podem se comunicar diretamente com o hardware porque o Linux não permite. O Linux divide a memória RAM em duas regiões: espaço de kernel (kernel space) e espaço de usúario (user space). O kernel space é onde o Linux executa e provêm seus serviços, e onde os device drivers residem. Já o user space é a região de memória onde os processos de usuários são executados. O kernel space pode ser acessado por processos de usuário somente através do uso de system calls (chamadas de sistema).

linux_kernel_space03.png

Desta forma, preferencialmente processos em espaço de kernel podem se comunicar com o hardware e acessar os periféricos. Você também pode usar drivers em espaço de usuário para acessar o hardware. Esse mecanismo permite aos desenvolvedores escreverem o código do software sem ter que se preocupar com detalhes de hardware. E protege o usúario de, inadvertidamente, acessar os dispositivos e de alguma maneira danificá-los. Isso tem funcionando bem até agora.

O que é Device Driver?

É um programa ou processo que é executado em uma região especial de memória e através do qual o usuário pode acessar um dispositivo ou recurso de um processador ou sistema computacional. Ele serve como um mediador entre o software e o hardware.

Por exemplo, quando você tira uma foto em seu celular Android, o driver da câmera interage com o software e passa informações a ele referentes às imagens capturadas e outras informações. O resultado final é a foto. Ou quando você quer jogar seu jogo preferido, mas não é possível sem o driver da placa de vídeo, certo? Por meio do driver o jogo acessa recursos da placa de vídeo que permitem a experiência incrível que os jogos proporcionam.

Então, a ideia é justamente essa. Servir como mediador entre o usuário e o hardware ou entre o software e o hardware.

Drivers como módulos ou embutidos no kernel

Os drivers no kernel Linux podem ser compilados como módulos que podem ser carregados em tempo de execução ou embutidos no próprio kernel.

Os drivers em módulos são conhecidos como Loadable Kernel Module (Módulo de Kernel Carregável) e se comportam de forma semelhante às DLLs do Windows. Um LKM (Loadable Kernel Module) se constitui de um único arquivo de objeto ELF, normalmente nomeado como “serial.o” para o kernel 2.4.x ou “serial.ko” para o kernel 2.6.x e versões posteriores. Uma grande vantagem é que ele pode ser carregado na memória em tempo de execução, bastando para isso um simples comando. E outra vantagem é que quando você altera o código do LKM, não é necessário compilar todo o kernel, basta compilar somente o LKM.

Os drivers embutidos ou monolíticos são módulos que são construídos como parte do kernel. Eles formam, juntamente com subsistemas do Linux e outros componentes, uma imagem única, cujo resultado final é o kernel propriamente dito. Vantagens de usar drivers monolíticos:

  • uma vez aceito no kernel Linux oficial, ele será mantido pelos desenvolvedores;

  • com custo de manutenção grátis, conserto de falhas de segurança e melhorias;

  • fácil acesso ao código fonte pelos usuários.

Device Tree

Device Tree é uma estrutura de dados para descrever hardware. Em vez de incluir código de hardware no sistema operacional, muitos aspectos do hardware podem ser descritos em uma estrutura de dados, que é passada ao kernel na hora do boot.

O Device Tree era usado até pouco tempo apenas em sistemas com processadores PowerPC e SPARC. Mas foi recentemente portado para processadores ARM no kernel 3.1 e seu uso é agora obrigatório no desenvolvimento de novos drivers. É uma grande vantagem para sistemas embarcados, porque podemos facilmente descrever diferentes tipos de placas que usam o mesmo processador ou ainda processadores diferentes.

A estrutura de dados em si é uma simples árvore de nós e propriedades com nomes. Os nós contêm propriedades e nós filhos. Propriedades são um par de valor-nome. Exemplo de descrição de um controlador SPI:

Com o Device Tree, é possível dar boot em qualquer placa com o mesmo kernel! Basta que os drivers do processador escolhido tenham suporte a Device Tree.

Toolchain

Caso você não saiba, da mesma forma que é necessário um compilador cruzado (cross compiler) ou toolchain para gerar executáveis para uma dada plataforma, você precisa de um compilador cruzado para gerar device drivers. Hoje em dia diversas distribuições Linux disponibilizam binários pré-compilados de compiladores cruzados e ferramentas de fácil instalação para uma variedade de arquiteturas. Você pode também compilar um manualmente se preferir.

O compilador usado vai depender da arquitetura do SoC (System-on-a-chip) escolhido. As plataformas ou arquiteturas mais utilizadas em Linux Embarcado atualmente são ARM, PowerPC e MIPS. Também é usado às vezes o x86 da Intel. Você precisa compilar ou baixar uma variante do gcc para o alvo especificado. É o que chamamos de compilador cruzado ou toolchain. Então para ARM nós teríamos algo como arm-linux-gcc. Para MIPS, mips-linux-gcc. E assim por diante.

Versão do kernel

“The 2.5 kernel implements a unified device driver model that will make driver development for 2.6 easier.”

A versão do kernel é muito importante quando se programa Linux device drivers. A API do kernel Linux muda constamente com o tempo. Um driver I2C escrito para a versão 2.6.30 do kernel não funciona para a versão 2.6.36 ou maior. E as diferenças entre a versão 2.4 e 2.6 são ainda maiores.

Na versão 2.5 do Linux os desenvolvedores criaram um Modelo Unificado de Device Drivers (Unified Device Driver Model) e que passou a vigorar na versão 2.6 do kernel. Ele consiste de um número de estruturas e funções comuns a todos os device drivers. E inclui suporte a gerenciamento de energia, comunicação com o espaço de usuário, dispositivos hotplug, classes de dispositivos, e outros mais. O Linux Device Driver Model é uma complexa estrutura de dados que reside na pasta /sys.

Fonte: Livro “Linux Device Drivers, 3º Edição” - Figura 14-1 (A small piece of the device model)

E sempre há mudanças na API do kernel ao longo do tempo. Por que a API do kernel muda constamente? Leia este documento para mais detalhes: https://www.kernel.org/doc/Documentation/stable_api_nonsense.txt

Uma coisa interessante sobre a evolução do kernel Linux é que as novas versões sempre adicionam novos atributos e nos anos recentes muitos atributos especifícos para sistemas embarcados tem sido adicionados. Um bom exemplo é o subsistema IIO (Industrial I/O). Ele foi designado para dar suporte a conversores A/D e D/A, acelerômetro, sensor de luz, sensor de proximidade, magnetrômetro, etc.

Para quem escreve código para software não importa a versão do kernel. Uma aplicação escrita para a versão 2.4.x roda nas versões 2.6.x, 3.x e possivelmente posteriores. Isso porque o Linux sempre mantém compatibilidade com versões anteriores em nível de aplicação.

Periféricos em Sistemas Embarcados

De uma perspectiva de device driver, desenvolvedores de software embarcado (firmware) frequentemente lidam com dispositivos que não são comumente encontrados em computadores convencionais. Exemplos de tais dispositivos: modem GSM, SPI, I2C, ADC, memória NAND, rádio, GPS.

ARM_architecture.png

Sob o Linux, existem essencialmente três tipos de dispositivos: dispositivos de rede, dispositivos de bloco e dispositivos de caractere. A maioria dos dispositivos se enquadram na categoria de dispositivos de caractere. Porém, hoje em dia, muitos device drivers não são implementados diretamente como dispositivos de caractere. Eles são desenvolvidos sob um framework, específico para um dado dispositivo. Exemplos de frameworks: framebuffer (gráficos), V4L2 (captura de vídeo), IIO (Industrial I/O), etc.

Por exemplo, se você deseja usar uma câmera para a qual não há suporte no Linux, você precisa escrever um device driver para essa câmera usando o framework Video4Linux2.

Interrupções

Se você já tem algum conhecimento ou experiência com sistemas embarcados, provavelmente sabe que interrupções é uma parte importante no desenvolvimento de firmwares. No entanto, infelizmente não é tão simples usar interrupções em Linux Embarcado. Só é possível usar interruções em drivers, ou seja, em espaço de kernel ou através de mecanismos de poll/select em espaço de usuário. Porém o uso de poll/select oferece poucos recursos.

A forma de interrupção que estamos acostumados a usar em sistemas embarcados só é possível em device drivers. Não se pode criar uma rotina ou função de tratamento de interrupção em um software. Outra alternativa é usar drivers em espaço de usuário.

Como em muitos outros sistemas operacionais, as interrupções são associadas a números e intervalos de endereços de entrada e saída. A programação de interrupções em Linux embarcado é semelhante a como é feito para Linux Desktop.

Escrevendo Device Drivers Portáveis

“Follow the kernel team's rules to make your drivers work on all architectures.”

Quando você trabalha com sistemas embarcados há uma boa chance de que use múltiplos tipos de processadores ao longo de sua carreira. É importante manter em mente que você deveria sempre que possível escrever device drivers portáveis. Quase todos os device drivers do Linux funcionam em mais de um tipo de processador. Para alcançar este objetivo, é necessario conhecer alguns conceitos como tipos de variavéis apropriadas, tamanhos de página de memória, problemas com ordem de byte, alinhamento de dados apropriado, e muitos outros.

Por exemplo, os processadores ARM dispõem de páginas de memória de 4K, 16K ou 32K, mas o i386 da Intel dispõe apenas de páginas de memória de 4K.

Mapeamento de Memória

Alguns processadores usam o método de Port-Mapped I/O (Entrada e Saída mapeados em porta) e tem instruções especiais para acessar portas e periféricos, como o x86 da Intel. Outros processadores usam o metodo de Memory-Mapped I/O (Entrada e Saída mapeados em memória), como o ARM, onde o acesso a portas e periféricos é feito diretamente através da memória RAM.

Para qualquer um dos métodos, MMIO ou PMIO, para poder acessar a memória RAM ou as portas de E/S no Linux, é necessário mapear a memória física para uma memória virtual. Para acessar a memória ou portas de maneira portável, você precisa chamar ioremap() para acessar uma região de memória e iounmap() para liberar uma região alocada anteriormente. No entanto essas funções estão sendo substituídas por devm_ioremap_resource() e futuramente se tornarão obsoletas. Se a alocação da região de memória não retornar um erro, pode-se então usar a família de funções read()/write() para ler e escrever a memória mapeada. Esse é o método típico para acessar os registradores de um processador que usa o método de MMIO.

Conclusão

Neste artigo procurei abordar os tópicos mais relevantes no desenvolvimento de drivers para Linux Embarcado e que apresentam diferenças entre a programação de drivers para Linux Desktop e para Linux Embarcado. Essas diferenças são mais perceptíveis quando se programa drivers de plataforma.

Referências

Linux Kernel and Driver Development Training - Free Electrons

Essential Linux Device Drivers

Linux Device Drivers – capitulo 14 (The Linux Device Model)

http://www.linuxjournal.com/article/5783
http://www.linuxjournal.com/article/6717
http://lwn.net/Articles/232575/
http://linux.die.net/man/2/poll
http://linuxembeded.blogspot.com.br/2014/04/embedded-linux-drivers.html

Saiba mais

Como se tornar um especialista em Linux embarcado 

Anatomia de um Sistema Linux embarcado 

Embedded Linux Build Systems 

Outros artigos da série

Linux Device Drivers - Diferenças entre Drivers de Plataforma e de Dispositivo >>
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Linux Embarcado » Device Drivers para Linux Embarcado - Introdução
Comentários:
Notificações
Notificar
guest
11 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Marcio Piai
Marcio Piai
12/11/2018 14:06

Temos uma versão Gentoo Linux V2.6.23 customizada que roda em uma CPU Industrial fabricada pela Kontron. Adquirimos um novo lote dessas CPU's industriais. No novo lote a versão 2.6 do linux não suporta o drive de rede ethernet. A Kontrol nos disse que seria necessário migrar para uma versão do linux no mínimo V3.13. Isto procede ou daria para resolver alterando a atual versão 2.6?

Vinicius Maciel
VINICIUS MACIEL
Reply to  Marcio Piai
13/11/2018 09:13

Você tem duas opções.
1. Desenvolver e adicionar o driver ethernet na versão 2.6.
2. Migrar para versão 3.13, supondo é claro que essa versão tenha o driver ethernet

O mais simples seria a opção 2. Mas certifique-se de que a versão 3.13 realmente tem o driver ethernet necessário.

Marcio Piai
Marcio Piai
Reply to  VINICIUS MACIEL
13/11/2018 13:12

Vinicius, Obrigado pela resposta.

Maike Reis
26/02/2018 13:13

Excelente artigo, parabéns!

André Castelan
29/04/2014 08:47

Artigo bem completo e legal de ser lido! Valeu

Katsu Abe
Katsu Abe
28/04/2014 09:02

Realmente muito interessante o funcionamento. Muito obrigado pela explicação.

Felype Nery
Felype Nery
25/04/2014 08:28

Excelente introdução ao tema! 🙂

Vinicius F. Maciel
Vinicius F. Maciel
Reply to  Felype Nery
26/04/2014 11:03

Obrigado. 😀 Estou planejando para o próximo artigo já colocar um exemplo. 😉

trackback
01/06/2015 01:11

[…] Device Drivers para Linux Embarcado - Introdução,  artigo escrito por Vinicius Maciel […]

trackback
02/10/2014 12:48

[…] - Device Drivers para Linux Embarcado – Introdução  […]

trackback
16/05/2014 00:01

[...] ponto importante para salientar é o uso do Device Tree. Como dito no artigo de introdução, é mandatório que todo driver de plataforma que é desenvolvido atualmente já inclua suporte a [...]

Talvez você goste:

Séries



Outros da Série

Menu

WEBINAR
 

Soluções inteligentes para acionamento de MOSFETs/IGBTs com família STDRIVE

Data: 08/10 às 15:00h - Apoio: STMicroelectronics
 
INSCREVA-SE AGORA »



 
close-link