Device Drivers USB no Linux

INSTITUTO TECNOLÓGICO DE AERONÁUTICA
Alunos: Igor Oliveira Aquino e Luty Rodrigues Ribeiro
Professor: Yano
Curso: 2º ano de Engenharia de Computação

Motivação

Este trabalho visa explicar o que é e como funciona um device driver. Perguntas como “O que é desejável de um device driver?”, “Como desenvolver um device driver no Linux?” e “Como implementar e testar um device driver USB?” serão respondidas ao longo do texto.

Introdução

A cada dia cresce mais o número de dispositivos que se conectam através do padrão USB (Universal Serial Bus), que está presente tanto em mouses e teclados quanto em media players e discos rígidos externos. Algumas das vantagens do padrão USB são:

  • Simples interface de conexão;
  • Permite dispositivos serem conectados e desconectados sem reiniciar o computador ou desligar o próprio dispositivo;
  • Provê energia elétrica para dispositivos de baixo consumo, eliminando a necessidade de alimentação externa.

Porém, por trás dessa simplicidade existe algo que gerencia a comunicação entre o dispositivo e o computador. É aí que entram os device drivers. Nesse trabalho, será dada ênfase na relação entre os device drivers e o sistema operacional Linux.

Device Drivers

Quase toda operação de sistema é mapeada para um dispositivo físico. Com exceção do processador, da memória e de mais alguns poucos elementos, toda e qualquer operação de controle de dispositivo é feita por um código que é específico para o dispositivo que está sendo acessado. Esse código é chamado device driver. Portanto, device drivers são a parte do sistema operacional que gerencia a comunicação com os dispositivos. A figura a seguir ilustra bem essa definição.

drivers.png

Os device drivers têm um papel especial no relacionamento com o kernel do Linux. Eles são os responsáveis por:

  • Fazer um hardware específico responder a comandos bem definidos vindos do sistema operacional;
  • Esconder completamente da interface de programação os detalhes de como o hardware funciona (evitando programação em baixo nível - uso de portas);
  • Aumentam a velocidade de I/O, porque eles geralmente são otimizados;
  • Gerenciam erros de hardware e software na comunicação;
  • Permitem o acesso simultâneo ao hardware por vários processos.

O kernel deve ter embutido nele os principais device drivers, como por exemplo aqueles que acessam o mouse e o teclado.
As atividades dos usuários são realizadas por meio de um conjunto de chamadas padronizadas que são independentes do driver em específico. Mapear essas chamadas padronizadas com o driver específico do hardware é o papel do device driver. Essa interface é tal que os drivers podem ser construídos separadamente do resto do kernel e então "plugados" em tempo de execução quando solicitados.

Outro ponto importante sobre device drivers é que eles devem ser responsáveis pelo mecanismo (ou funcionamento) e não pelo controle de acesso aos dispositivos. Por exemplo, um driver deve fazer um disco rígido externo ser acessado, mas quem controla as permissões de acesso a esse disco não é o driver propriamente dito. Resumindo, o programador de um driver deve se preocupar em fazer o dispositivo funcionar e deixar para a aplicação a tarefa de controlar o uso do dispositivo.
Apesar disso, muitos drivers são lançados juntamente com um software que ajuda o usuário a configurar seu dispositivo. Estes softwares podem variar desde simples opções que devem ser setadas na linha de comando até sofisticadas telas de interface com várias opções de customização possíveis.

Classificação de Drivers

Pode-se classificar os drivers em quatro categorias segundo o tipo de dado que é gerenciado por eles e como eles o fazem. Os tipos são:

  • Character Drivers: Transmitem informações do usuário para o dispositivo (ou vice-versa) byte a byte. Ex.: impressora (/dev/lp);
  • Block Drivers: Transmitem informação bloco por bloco. Ou seja, os dados vão chegando e sendo armazenados num buffer e, quando este está lotado, ocorre a tranmissão. Ex.: Discos Rígidos IDE (/dev/fd0);
  • Terminal Drivers: São drivers que, por exemplo, gerenciam janelas de ambiente e o console;
  • Streams: Feitos para altas velocidade de tráfego de dados.

Implementação e Instalação de Device Drivers

Implementar um driver não é uma tarefa tão simples. Por exemplo, ela requer habilidade com programação em baixo nível (portas e interrupções) e, para debugar um programa, não se pode usar debuggers ou funções do C como printf. Para construir um driver, deve-se seguir os passos abaixo:

  • Programar os arquivos fonte do driver, tendo especial cuidado com a interface com o kernel;
  • Integrar o driver no kernel, incluindo na fonte do kernel chamadas para as funções do driver;
  • Configure e compile o novo kernel;
  • Teste o driver escrevendo um programa de usuário.

A primeira tarefa para implementar o código fonte de um driver é selecionar um nome que o identifique unicamente. No nosso caso, suponha que o nome do driver seja yano. Considerando que o driver sejá do tipo character driver, seu código fonte deverá fica na pasta /usr/src/linux/drivers/char/yano.c, e seu cabeçalho em /usr/include/linux/yano.h.

A segunda tarefa é implementar as funções. Nesse ponto, vale lembrar que as funções da biblioteca padrão do C (stdio.h) não estão disponíveis. Além disso, o tamanho da pilha é limitado e algumas operações com ponto flutuante não podem ser realizadas.

Após escrever o código do driver, é necessário integrá-lo ao kernel. Para isso, basta fazer os seguintes passos:

  • Adicione chamados do kernel para o novo driver;
  • Adicione o driver à lista de drivers;
  • Altere os scripts de compilação;
  • Compile o driver novamente.
CamadasUSB.png

O kernel do Linux provê a API USB core, que gerencia grande parte da complexidade. Ela possui uma espécie de canal chamado Endpoint, a forma mais básica de comunicação USB.
Os endpoints USB só transferem dados numa única direção. São elas:

  • Do computador para o dispositivo – OUT endpoint
  • Do dispositivo para o computador – IN endpoint

Os endpoints podem ser classificados quanto ao modo de transmissão de dados. Suas categorias são:

  • Control – são usados para informações de configuração do próprio dispositivo. Todo dispositivo USB possui um endpoint chamado “endpoint 0”, usado pelo USB core para configurar o dispositivo em “tempo de inserção”.
  • Interrupt – usados para transmitir pequenas quantidades de dados, a cada vez que o computador pede algum dado ao dispositivo. Estes endpoints foram primeiro método de transporte de dados para teclados e mouses USB.
  • Bulk – estes endpoints são usados para transferir grandes quantidades de dados. São normalmente bem maiores que os “Interrupt endpoints”. São utilizados quando se necessita de uma transferência de grande quantidade de dados sem perdas. Utilizados em impressoras, dispositivos de armazenamento.
  • Isochronous – também pode transferir grande quantidade de dados, mas não tem a garantia de transmissão de todos os dados como o bulk endpoint. Entretanto, pode manter uma taxa de transferência de dados constante. Mais usados em dispositivos de tempo real, como dispositivos de áudio e vídeo.

Control e Bulk são utilizados para transferências assíncronas, enquanto Interrpt e Isochronous são usados em transferências síncronas.

Os endpoints USB são encapsulados em interfaces. É esta interface que o USB core passa ao USB driver e que o USB driver utiliza para fazer o devido controle.

As interfaces USB, por sua vez, são encapsuladas em configurações. Um dispositivo USB pode ter várias configurações alternando entre elas para mudar o estado do dispositivo.

Nos sistemas Linux, os device drivers são distribuídos normalmente de três modos:

  • Um patch para uma versão específica de kernel;
  • Um módulo carregável;
  • Um script de instalação que aplica os patches apropriados.

O meio mais comum é o primeiro, através do patch para aquela versão. Geralmente, os patches são aplicados pelo comando:
cd usr/src/linux ; patch –p1 < arquivo_patch

Teste de Device Drivers

Quando for necessário realizar testes, caso não esteja disponível o hardware correspondente ao device driver projetado, a idéia para o teste é testar o fluxo das várias funções auxiliares nas baixas camadas e testar os pontos de entrada do device driver. O código de teste verificará a lógica e os fluxos de controle utilizados. Para os pontos de entrada do dispositivo e funções auxiliares, substituímos por funções dummy (seria uma função com o mínimo código necessário, apenas para atender ao tipo de retorno exigido). Desta forma, nós estamos, no fundo, testando o device driver com um hardware virtual, utilizando as funções dummy.

Caso o hardware já esteja disponível, podemos então remover as funções dummy utilizadas na fase inicial de testes e os testes são feitos utilizando o próprio hardware.

Entretanto, é importante efetuar os testes antes de conectar o hardware, utilizando as funções dummy. Isto evita, por exemplo, que o dispositivo seja conectado num sistema que eventualmente apresente algum bug, atrapalhando assim o teste efetivo do device driver com o seu respectivo dispositivo. Essas possíveis falhas seriam descobertas na fase inicial de teste.

Referências

MATIA, Fernando. Writing a Linux Driver. Linux Journal, Madrid, n. , p.1-7, 01 abr. 1998. Disponível em: <http://www.linuxjournal.com/article/2476>. Acesso em: 07 jul. 2009.

ANÔNIMO. Universal Serial Bus. Wikipedia, Internet, n. , p.1-1, 01 abr. 2002. Disponível em: <http://en.wikipedia.org/wiki/Usb>. Acesso em: 07 jul. 2009.

CORBET, Jonathan; RUBINI, Alessandro; KROAH-HARTMAN, Greg. An Introduction to Device Drivers. Linux Device Drivers: Third Edition, Sebastopol, n. , p.1-5, 27 jan. 2005.

ANÔNIMO. Linux process size. Forums-x, Internet, n. , p.1-1, 30 dez. 2007. Disponível em: <http://forums.x-plane.org/index.php?showtopic=29584>. Acesso em: 07 jul. 2009.

ANÔNIMO. Debugging simulated hardware on Linux: Part 1. Ibm Developerworks, Internet, n. , p.1-1, 30 dez. 2007. Disponível em: <http://www.ibm.com/developerworks/linux/library/l-devdebug.html>. Acesso em: 07 jul. 2009.

ANÔNIMO. Linux Drivers and the Kernel. Inform It, Internet, n. , p.1-1, 26 set. 2002. Disponível em: <http://www.informit.com/articles/article.aspx?p=29445&seqNum=6>. Acesso em: 07 jul. 2009.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License