Drivers de dispostivos_linux

50
Drivers de Dispositivos Linux 2 de Setembro de 2008

Transcript of Drivers de dispostivos_linux

Drivers de Dispositivos Linux

2 de Setembro de 2008

Conteúdo

I Sobre essa apostila 2

II Informações Básicas 4

III GNU Free Documentation License 9

IV Driver de Dsipositivo Linux 18

1 Driver de Dispositivo Linux 19

2 Plano de ensino 202.1 Objetivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.2 Público Alvo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.3 Pré-requisitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.4 Descrição . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.5 Cronograma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.6 Programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.7 Avaliação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.8 Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

3 Introdução 233.1 Driver de Dispositivo? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233.2 Tipos de Dispositivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.3 Interface do Driver com o Sistema Operacional . . . . . . . . . . . . . . . . . . . . . 24

4 Hello Kernel! 264.1 Kello Hernell! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264.2 Como funciona? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

5 Arquivos de Dispositivo 285.1 Tudo é um arquivo! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285.2 Major e Minor Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

6 Estruturas 316.1 File Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

6.1.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316.1.2 Fops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

1

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

6.1.3 Inicializando a fops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346.1.4 Algumas operações de mentirinha . . . . . . . . . . . . . . . . . . . . . . . . 35

6.2 Filp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376.3 Estrutura de Inode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

6.3.1 Links e inodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386.3.2 inode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

7 Mydev 40

8 A memória e o Kernel 438.1 Como funciona a memória? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

9 Mydev completo 459.1 mydev.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459.2 Utilização do mydev . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

2

Parte I

Sobre essa apostila

3

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

Conteúdo

O conteúdo dessa apostila é fruto da compilação de diversos materiais livres publicados na in-ternet, disponíveis em diversos sites ou originalmente produzido no CDTC em http://www.cdtc.org.br.

O formato original deste material bem como sua atualização está disponível dentro da licençaGNU Free Documentation License, cujo teor integral encontra-se aqui reproduzido na seção demesmo nome, tendo inclusive uma versão traduzida (não oficial).

A revisão e alteração vem sendo realizada pelo CDTC ([email protected]), desde outubrode 2006. Criticas e sugestões construtivas são bem-vindas a qualquer tempo.

Autores

A autoria deste conteúdo, atividades e avaliações é de responsabilidade de Leonardo Gui-lherme de Freitas ([email protected]) .

O texto original faz parte do projeto Centro de Difusão de Tecnolgia e Conhecimento, que vemsendo realizado pelo ITI em conjunto com outros parceiros institucionais, atuando em conjuntocom as universidades federais brasileiras que tem produzido e utilizado Software Livre, apoiandoinclusive a comunidade Free Software junto a outras entidades no país.

Informações adicionais podem ser obtidas atréves do email [email protected], ou dahome page da entidade, através da URL http://www.cdtc.org.br .

Garantias

O material contido nesta apostila é isento de garantias e o seu uso é de inteira responsabi-lidade do usuário/leitor. Os autores, bem como o ITI e seus parceiros, não se responsabilizamdireta ou indiretamente por qualquer prejuízo oriundo da utilização do material aqui contido.

Licença

Copyright ©2006,Leonardo Guilherme de Freitas ([email protected]) .

Permission is granted to copy, distribute and/or modify this document under the termsof the GNU Free Documentation License, Version 1.1 or any later version published bythe Free Software Foundation; with the Invariant Chapter being SOBRE ESSA APOS-TILA. A copy of the license is included in the section entitled GNU Free DocumentationLicense.

4

Parte II

Informações Básicas

5

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

Sobre o CDTC

Objetivo Geral

O Projeto CDTC visa a promoção e o desenvolvimento de ações que incentivem a dissemina-ção de soluções que utilizem padrões abertos e não proprietários de tecnologia, em proveito dodesenvolvimento social, cultural, político, tecnológico e econômico da sociedade brasileira.

Objetivo Específico

Auxiliar o Governo Federal na implantação do plano nacional de software não-proprietário ede código fonte aberto, identificando e mobilizando grupos de formadores de opinião dentre osservidores públicos e agentes políticos da União Federal, estimulando e incentivando o mercadonacional a adotar novos modelos de negócio da tecnologia da informação e de novos negóciosde comunicação com base em software não-proprietário e de código fonte aberto, oferecendotreinamento específico para técnicos, profissionais de suporte e funcionários públicos usuários,criando grupos de funcionários públicos que irão treinar outros funcionários públicos e atuar comoincentivadores e defensores de produtos de software não proprietários e código fonte aberto, ofe-recendo conteúdo técnico on-line para serviços de suporte, ferramentas para desenvolvimento deprodutos de software não proprietários e de seu código fonte livre, articulando redes de terceiros(dentro e fora do governo) fornecedoras de educação, pesquisa, desenvolvimento e teste de pro-dutos de software livre.

Guia do aluno

Neste guia, você terá reunidas uma série de informações importantes para que você comeceseu curso. São elas:

• Licenças para cópia de material disponível

• Os 10 mandamentos do aluno de Educação a Distância

• Como participar dos fóruns e da wikipédia

• Primeiros passos

É muito importante que você entre em contato com TODAS estas informações, seguindo oroteiro acima.

Licença

Copyright ©2006, Leonardo Guilherme de Freitas ([email protected]) .

6

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

É dada permissão para copiar, distribuir e/ou modificar este documento sob os termosda Licença de Documentação Livre GNU, Versão 1.1 ou qualquer versão posteriorpublicada pela Free Software Foundation; com o Capítulo Invariante SOBRE ESSAAPOSTILA. Uma cópia da licença está inclusa na seção entitulada "Licença de Docu-mentação Livre GNU".

Os 10 mandamentos do aluno de educação online

• 1. Acesso a Internet: ter endereço eletrônico, um provedor e um equipamento adequado épré-requisito para a participação nos cursos a distância.

• 2. Habilidade e disposição para operar programas: ter conhecimentos básicos de Informá-tica é necessário para poder executar as tarefas.

• 3. Vontade para aprender colaborativamente: interagir, ser participativo no ensino a distân-cia conta muitos pontos, pois irá colaborar para o processo ensino-aprendizagem pessoal,dos colegas e dos professores.

• 4. Comportamentos compatíveis com a etiqueta: mostrar-se interessado em conhecer seuscolegas de turma respeitando-os e fazendo ser respeitado pelo mesmo.

• 5. Organização pessoal: planejar e organizar tudo é fundamental para facilitar a sua revisãoe a sua recuperação de materiais.

• 6. Vontade para realizar as atividades no tempo correto: anotar todas as suas obrigações erealizá-las em tempo real.

• 7. Curiosidade e abertura para inovações: aceitar novas idéias e inovar sempre.

• 8. Flexibilidade e adaptação: requisitos necessário a mudança tecnológica, aprendizagense descobertas.

• 9. Objetividade em sua comunicação: comunicar-se de forma clara, breve e transparente éponto-chave na comunicação pela Internet.

• 10. Responsabilidade: ser responsável por seu próprio aprendizado. O ambiente virtual nãocontrola a sua dedicação, mas reflete os resultados do seu esforço e da sua colaboração.

Como participar dos fóruns e Wikipédia

Você tem um problema e precisa de ajuda?

Podemos te ajudar de 2 formas:

A primeira é o uso dos fóruns de notícias e de dúvidas gerais que se distinguem pelo uso:

O fórum de notícias tem por objetivo disponibilizar um meio de acesso rápido a informaçõesque sejam pertinentes ao curso (avisos, notícias). As mensagens postadas nele são enviadas a

7

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

todos participantes. Assim, se o monitor ou algum outro participante tiver uma informação queinteresse ao grupo, favor postá-la aqui.Porém, se o que você deseja é resolver alguma dúvida ou discutir algum tópico específico docurso, é recomendado que você faça uso do Fórum de dúvidas gerais que lhe dá recursos maisefetivos para esta prática.

. O fórum de dúvidas gerais tem por objetivo disponibilizar um meio fácil, rápido e interativopara solucionar suas dúvidas e trocar experiências. As mensagens postadas nele são enviadasa todos participantes do curso. Assim, fica muito mais fácil obter respostas, já que todos podemajudar.Se você receber uma mensagem com algum tópico que saiba responder, não se preocupe com aformalização ou a gramática. Responda! E não se esqueça de que antes de abrir um novo tópicoé recomendável ver se a sua pergunta já foi feita por outro participante.

A segunda forma se dá pelas Wikis:

Uma wiki é uma página web que pode ser editada colaborativamente, ou seja, qualquer par-ticipante pode inserir, editar, apagar textos. As versões antigas vão sendo arquivadas e podemser recuperadas a qualquer momento que um dos participantes o desejar. Assim, ela oferece umótimo suporte a processos de aprendizagem colaborativa. A maior wiki na web é o site "Wikipé-dia", uma experiência grandiosa de construção de uma enciclopédia de forma colaborativa, porpessoas de todas as partes do mundo. Acesse-a em português pelos links:

• Página principal da Wiki - http://pt.wikipedia.org/wiki/

Agradecemos antecipadamente a sua colaboração com a aprendizagem do grupo!

Primeiros Passos

Para uma melhor aprendizagem é recomendável que você siga os seguintes passos:

• Ler o Plano de Ensino e entender a que seu curso se dispõe a ensinar;

• Ler a Ambientação do Moodle para aprender a navegar neste ambiente e se utilizar dasferramentas básicas do mesmo;

• Entrar nas lições seguindo a seqüência descrita no Plano de Ensino;

• Qualquer dúvida, reporte ao Fórum de Dúvidas Gerais.

Perfil do Tutor

Segue-se uma descrição do tutor ideal, baseada no feedback de alunos e de tutores.

O tutor ideal é um modelo de excelência: é consistente, justo e profissional nos respectivosvalores e atitudes, incentiva mas é honesto, imparcial, amável, positivo, respeitador, aceita asidéias dos estudantes, é paciente, pessoal, tolerante, apreciativo, compreensivo e pronto a ajudar.

8

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

A classificação por um tutor desta natureza proporciona o melhor feedback possível, é crucial, e,para a maior parte dos alunos, constitui o ponto central do processo de aprendizagem.’ Este tutorou instrutor:

• fornece explicações claras acerca do que ele espera, e do estilo de classificação que iráutilizar;

• gosta que lhe façam perguntas adicionais;

• identifica as nossas falhas, mas corrige-as amavelmente’, diz um estudante, ’e explica por-que motivo a classificação foi ou não foi atribuída’;

• tece comentários completos e construtivos, mas de forma agradável (em contraste com umreparo de um estudante: ’os comentários deixam-nos com uma sensação de crítica, deameaça e de nervosismo’)

• dá uma ajuda complementar para encorajar um estudante em dificuldade;

• esclarece pontos que não foram entendidos, ou corretamente aprendidos anteriormente;

• ajuda o estudante a alcançar os seus objetivos;

• é flexível quando necessário;

• mostra um interesse genuíno em motivar os alunos (mesmo os principiantes e, por isso,talvez numa fase menos interessante para o tutor);

• escreve todas as correções de forma legível e com um nível de pormenorização adequado;

• acima de tudo, devolve os trabalhos rapidamente;

9

Parte III

GNU Free Documentation License

10

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

(Traduzido pelo João S. O. Bueno através do CIPSGA em 2001)Esta é uma tradução não oficial da Licençaa de Documentação Livre GNU em Português

Brasileiro. Ela não é publicada pela Free Software Foundation, e não se aplica legalmente a dis-tribuição de textos que usem a GFDL - apenas o texto original em Inglês da GNU FDL faz isso.Entretanto, nós esperamos que esta tradução ajude falantes de português a entenderem melhora GFDL.

This is an unofficial translation of the GNU General Documentation License into Brazilian Por-tuguese. It was not published by the Free Software Foundation, and does not legally state thedistribution terms for software that uses the GFDL–only the original English text of the GFDL doesthat. However, we hope that this translation will help Portuguese speakers understand the GFDLbetter.

Licença de Documentação Livre GNU Versão 1.1, Março de 2000

Copyright (C) 2000 Free Software Foundation, Inc.59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

É permitido a qualquer um copiar e distribuir cópias exatas deste documento de licença, masnão é permitido alterá-lo.

INTRODUÇÃO

O propósito desta Licença é deixar um manual, livro-texto ou outro documento escrito "livre"nosentido de liberdade: assegurar a qualquer um a efetiva liberdade de copiá-lo ou redistribui-lo,com ou sem modificações, comercialmente ou não. Secundariamente, esta Licença mantémpara o autor e editor uma forma de ter crédito por seu trabalho, sem ser considerado responsávelpelas modificações feitas por terceiros.

Esta Licença é um tipo de "copyleft"("direitos revertidos"), o que significa que derivações dodocumento precisam ser livres no mesmo sentido. Ela complementa a GNU Licença Pública Ge-ral (GNU GPL), que é um copyleft para software livre.

Nós fizemos esta Licença para que seja usada em manuais de software livre, por que softwarelivre precisa de documentação livre: um programa livre deve ser acompanhado de manuais queprovenham as mesmas liberdades que o software possui. Mas esta Licença não está restrita amanuais de software; ela pode ser usada para qualquer trabalho em texto, independentementedo assunto ou se ele é publicado como um livro impresso. Nós recomendamos esta Licença prin-cipalmente para trabalhos cujo propósito seja de introdução ou referência.

APLICABILIDADE E DEFINIÇÕES

Esta Licença se aplica a qualquer manual ou outro texto que contenha uma nota colocada pelodetentor dos direitos autorais dizendo que ele pode ser distribuído sob os termos desta Licença.

11

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

O "Documento"abaixo se refere a qualquer manual ou texto. Qualquer pessoa do público é umlicenciado e é referida como "você".

Uma "Versão Modificada"do Documento se refere a qualquer trabalho contendo o documentoou uma parte dele, quer copiada exatamente, quer com modificações e/ou traduzida em outralíngua.

Uma "Seção Secundária"é um apêndice ou uma seção inicial do Documento que trata ex-clusivamente da relação dos editores ou dos autores do Documento com o assunto geral doDocumento (ou assuntos relacionados) e não contém nada que poderia ser incluído diretamentenesse assunto geral (Por exemplo, se o Documento é em parte um livro texto de matemática, aSeção Secundária pode não explicar nada de matemática).

Essa relação poderia ser uma questão de ligação histórica com o assunto, ou matérias relaci-onadas, ou de posições legais, comerciais, filosóficas, éticas ou políticas relacionadas ao mesmo.

As "Seções Invariantes"são certas Seções Secundárias cujos títulos são designados, comosendo de Seções Invariantes, na nota que diz que o Documento é publicado sob esta Licença.

Os "Textos de Capa"são certos trechos curtos de texto que são listados, como Textos de CapaFrontal ou Textos da Quarta Capa, na nota que diz que o texto é publicado sob esta Licença.

Uma cópia "Transparente"do Documento significa uma cópia que pode ser lida automatica-mente, representada num formato cuja especificação esteja disponível ao público geral, cujosconteúdos possam ser vistos e editados diretamente e sem mecanismos especiais com editoresde texto genéricos ou (para imagens compostas de pixels) programas de pintura genéricos ou(para desenhos) por algum editor de desenhos grandemente difundido, e que seja passível deservir como entrada a formatadores de texto ou para tradução automática para uma variedadede formatos que sirvam de entrada para formatadores de texto. Uma cópia feita em um formatode arquivo outrossim Transparente cuja constituição tenha sido projetada para atrapalhar ou de-sencorajar modificações subsequentes pelos leitores não é Transparente. Uma cópia que não é"Transparente"é chamada de "Opaca".

Exemplos de formatos que podem ser usados para cópias Transparentes incluem ASCII sim-ples sem marcações, formato de entrada do Texinfo, formato de entrada do LaTex, SGML ou XMLusando uma DTD disponibilizada publicamente, e HTML simples, compatível com os padrões, eprojetado para ser modificado por pessoas. Formatos opacos incluem PostScript, PDF, formatosproprietários que podem ser lidos e editados apenas com processadores de texto proprietários,SGML ou XML para os quais a DTD e/ou ferramentas de processamento e edição não estejamdisponíveis para o público, e HTML gerado automaticamente por alguns editores de texto comfinalidade apenas de saída.

A "Página do Título"significa, para um livro impresso, a página do título propriamente dita,mais quaisquer páginas subsequentes quantas forem necessárias para conter, de forma legível,o material que esta Licença requer que apareça na página do título. Para trabalhos que nãotenham uma página do título, "Página do Título"significa o texto próximo da aparição mais proe-minente do título do trabalho, precedendo o início do corpo do texto.

12

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

FAZENDO CÓPIAS EXATAS

Você pode copiar e distribuir o Documento em qualquer meio, de forma comercial ou nãocomercial, desde que esta Licença, as notas de copyright, e a nota de licença dizendo que estaLicença se aplica ao documento estejam reproduzidas em todas as cópias, e que você não acres-cente nenhuma outra condição, quaisquer que sejam, às desta Licença.

Você não pode usar medidas técnicas para obstruir ou controlar a leitura ou confecção decópias subsequentes das cópias que você fizer ou distribuir. Entretanto, você pode aceitar com-pensação em troca de cópias. Se você distribuir uma quantidade grande o suficiente de cópias,você também precisa respeitar as condições da seção 3.

Você também pode emprestar cópias, sob as mesmas condições colocadas acima, e tambémpode exibir cópias publicamente.

FAZENDO CÓPIAS EM QUANTIDADE

Se você publicar cópias do Documento em número maior que 100, e a nota de licença doDocumento obrigar Textos de Capa, você precisará incluir as cópias em capas que tragam, clarae legivelmente, todos esses Textos de Capa: Textos de Capa da Frente na capa da frente, eTextos da Quarta Capa na capa de trás. Ambas as capas também precisam identificar clara elegivelmente você como o editor dessas cópias. A capa da frente precisa apresentar o titulo com-pleto com todas as palavras do título igualmente proeminentes e visíveis. Você pode adicionaroutros materiais às capas. Fazer cópias com modificações limitadas às capas, tanto quanto estaspreservem o título do documento e satisfaçam a essas condições, pode ser tratado como cópiaexata em outros aspectos.

Se os textos requeridos em qualquer das capas for muito volumoso para caber de formalegível, você deve colocar os primeiros (tantos quantos couberem de forma razoável) na capaverdadeira, e continuar os outros nas páginas adjacentes.

Se você publicar ou distribuir cópias Opacas do Documento em número maior que 100, vocêprecisa ou incluir uma cópia Transparente que possa ser lida automaticamente com cada cópiaOpaca, ou informar, em ou com, cada cópia Opaca a localização de uma cópia Transparentecompleta do Documento acessível publicamente em uma rede de computadores, a qual o públicousuário de redes tenha acesso a download gratuito e anônimo utilizando padrões públicos deprotocolos de rede. Se você utilizar o segundo método, você precisará tomar cuidados razoavel-mente prudentes, quando iniciar a distribuição de cópias Opacas em quantidade, para assegurarque esta cópia Transparente vai permanecer acessível desta forma na localização especificadapor pelo menos um ano depois da última vez em que você distribuir uma cópia Opaca (direta-mente ou através de seus agentes ou distribuidores) daquela edição para o público.

É pedido, mas não é obrigatório, que você contate os autores do Documento bem antes deredistribuir qualquer grande número de cópias, para lhes dar uma oportunidade de prover vocêcom uma versão atualizada do Documento.

13

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

MODIFICAÇÕES

Você pode copiar e distribuir uma Versão Modificada do Documento sob as condições das se-ções 2 e 3 acima, desde que você publique a Versão Modificada estritamente sob esta Licença,com a Versão Modificada tomando o papel do Documento, de forma a licenciar a distribuiçãoe modificação da Versão Modificada para quem quer que possua uma cópia da mesma. Alémdisso, você precisa fazer o seguinte na versão modificada:

A. Usar na Página de Título (e nas capas, se houver alguma) um título distinto daquele do Do-cumento, e daqueles de versões anteriores (que deveriam, se houvesse algum, estarem listadosna seção "Histórico do Documento"). Você pode usar o mesmo título de uma versão anterior seo editor original daquela versão lhe der permissão;

B. Listar na Página de Título, como autores, uma ou mais das pessoas ou entidades responsá-veis pela autoria das modificações na Versão Modificada, conjuntamente com pelo menos cincodos autores principais do Documento (todos os seus autores principais, se ele tiver menos quecinco);

C. Colocar na Página de Título o nome do editor da Versão Modificada, como o editor;

D. Preservar todas as notas de copyright do Documento;

E. Adicionar uma nota de copyright apropriada para suas próprias modificações adjacente àsoutras notas de copyright;

F. Incluir, imediatamente depois das notas de copyright, uma nota de licença dando ao públicoo direito de usar a Versão Modificada sob os termos desta Licença, na forma mostrada no tópicoabaixo;

G. Preservar nessa nota de licença as listas completas das Seções Invariantes e os Textos deCapa requeridos dados na nota de licença do Documento;

H. Incluir uma cópia inalterada desta Licença;

I. Preservar a seção entitulada "Histórico", e seu título, e adicionar à mesma um item dizendopelo menos o título, ano, novos autores e editor da Versão Modificada como dados na Página deTítulo. Se não houver uma sessão denominada "Histórico"no Documento, criar uma dizendo otítulo, ano, autores, e editor do Documento como dados em sua Página de Título, então adicionarum item descrevendo a Versão Modificada, tal como descrito na sentença anterior;

J. Preservar o endereço de rede, se algum, dado no Documento para acesso público a umacópia Transparente do Documento, e da mesma forma, as localizações de rede dadas no Docu-mento para as versões anteriores em que ele foi baseado. Elas podem ser colocadas na seção"Histórico". Você pode omitir uma localização na rede para um trabalho que tenha sido publicadopelo menos quatro anos antes do Documento, ou se o editor original da versão a que ela se refirader sua permissão;

K. Em qualquer seção entitulada "Agradecimentos"ou "Dedicatórias", preservar o título da

14

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

seção e preservar a seção em toda substância e fim de cada um dos agradecimentos de contri-buidores e/ou dedicatórias dados;

L. Preservar todas as Seções Invariantes do Documento, inalteradas em seus textos ou emseus títulos. Números de seção ou equivalentes não são considerados parte dos títulos da seção;

M. Apagar qualquer seção entitulada "Endossos". Tal sessão não pode ser incluída na VersãoModificada;

N. Não reentitular qualquer seção existente com o título "Endossos"ou com qualquer outrotítulo dado a uma Seção Invariante.

Se a Versão Modificada incluir novas seções iniciais ou apêndices que se qualifiquem comoSeções Secundárias e não contenham nenhum material copiado do Documento, você pode optarpor designar alguma ou todas aquelas seções como invariantes. Para fazer isso, adicione seustítulos à lista de Seções Invariantes na nota de licença da Versão Modificada. Esses títulos preci-sam ser diferentes de qualquer outro título de seção.

Você pode adicionar uma seção entitulada "Endossos", desde que ela não contenha qual-quer coisa além de endossos da sua Versão Modificada por várias pessoas ou entidades - porexemplo, declarações de revisores ou de que o texto foi aprovado por uma organização como adefinição oficial de um padrão.

Você pode adicionar uma passagem de até cinco palavras como um Texto de Capa da Frente, e uma passagem de até 25 palavras como um Texto de Quarta Capa, ao final da lista de Textosde Capa na Versão Modificada. Somente uma passagem de Texto da Capa da Frente e uma deTexto da Quarta Capa podem ser adicionados por (ou por acordos feitos por) qualquer entidade.Se o Documento já incluir um texto de capa para a mesma capa, adicionado previamente porvocê ou por acordo feito com alguma entidade para a qual você esteja agindo, você não podeadicionar um outro; mas você pode trocar o antigo, com permissão explícita do editor anterior queadicionou a passagem antiga.

O(s) autor(es) e editor(es) do Documento não dão permissão por esta Licença para que seusnomes sejam usados para publicidade ou para assegurar ou implicar endossamento de qualquerVersão Modificada.

COMBINANDO DOCUMENTOS

Você pode combinar o Documento com outros documentos publicados sob esta Licença, sobos termos definidos na seção 4 acima para versões modificadas, desde que você inclua na com-binação todas as Seções Invariantes de todos os documentos originais, sem modificações, e listetodas elas como Seções Invariantes de seu trabalho combinado em sua nota de licença.

O trabalho combinado precisa conter apenas uma cópia desta Licença, e Seções InvariantesIdênticas com multiplas ocorrências podem ser substituídas por apenas uma cópia. Se houvermúltiplas Seções Invariantes com o mesmo nome mas com conteúdos distintos, faça o título de

15

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

cada seção único adicionando ao final do mesmo, em parênteses, o nome do autor ou editororigianl daquela seção, se for conhecido, ou um número que seja único. Faça o mesmo ajustenos títulos de seção na lista de Seções Invariantes nota de licença do trabalho combinado.

Na combinação, você precisa combinar quaisquer seções entituladas "Histórico"dos diver-sos documentos originais, formando uma seção entitulada "Histórico"; da mesma forma combinequaisquer seções entituladas "Agradecimentos", ou "Dedicatórias". Você precisa apagar todas asseções entituladas como "Endosso".

COLETÂNEAS DE DOCUMENTOS

Você pode fazer uma coletânea consitindo do Documento e outros documentos publicadossob esta Licença, e substituir as cópias individuais desta Licença nos vários documentos comuma única cópia incluida na coletânea, desde que você siga as regras desta Licença para cópiaexata de cada um dos Documentos em todos os outros aspectos.

Você pode extrair um único documento de tal coletânea, e distribuí-lo individualmente sobesta Licença, desde que você insira uma cópia desta Licença no documento extraído, e siga estaLicença em todos os outros aspectos relacionados à cópia exata daquele documento.

AGREGAÇÃO COM TRABALHOS INDEPENDENTES

Uma compilação do Documento ou derivados dele com outros trabalhos ou documentos se-parados e independentes, em um volume ou mídia de distribuição, não conta como uma Ver-são Modificada do Documento, desde que nenhum copyright de compilação seja reclamado pelacompilação. Tal compilação é chamada um "agregado", e esta Licença não se aplica aos outrostrabalhos auto-contidos compilados junto com o Documento, só por conta de terem sido assimcompilados, e eles não são trabalhos derivados do Documento.

Se o requerido para o Texto de Capa na seção 3 for aplicável a essas cópias do Documento,então, se o Documento constituir menos de um quarto de todo o agregado, os Textos de Capado Documento podem ser colocados em capas adjacentes ao Documento dentro do agregado.Senão eles precisarão aparecer nas capas de todo o agregado.

TRADUÇÃO

Tradução é considerada como um tipo de modificação, então você pode distribuir traduçõesdo Documento sob os termos da seção 4. A substituição de Seções Invariantes por traduçõesrequer uma permissão especial dos detentores do copyright das mesmas, mas você pode incluirtraduções de algumas ou de todas as Seções Invariantes em adição às versões orignais dessasSeções Invariantes. Você pode incluir uma tradução desta Licença desde que você também in-clua a versão original em Inglês desta Licença. No caso de discordância entre a tradução e a

16

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

versão original em Inglês desta Licença, a versão original em Inglês prevalecerá.

TÉRMINO

Você não pode copiar, modificar, sublicenciar, ou distribuir o Documento exceto como expres-samente especificado sob esta Licença. Qualquer outra tentativa de copiar, modificar, sublicen-ciar, ou distribuir o Documento é nula, e resultará automaticamente no término de seus direitossob esta Licença. Entretanto, terceiros que tenham recebido cópias, ou direitos de você sob estaLicença não terão suas licenças terminadas, tanto quanto esses terceiros permaneçam em totalacordo com esta Licença.

REVISÕES FUTURAS DESTA LICENÇA

A Free Software Foundation pode publicar novas versões revisadas da Licença de Documen-tação Livre GNU de tempos em tempos. Tais novas versões serão similares em espirito à versãopresente, mas podem diferir em detalhes ao abordarem novos porblemas e preocupações. Vejahttp://www.gnu.org/copyleft/.

A cada versão da Licença é dado um número de versão distinto. Se o Documento especificarque uma versão particular desta Licença "ou qualquer versão posterior"se aplica ao mesmo, vocêtem a opção de seguir os termos e condições daquela versão específica, ou de qualquer versãoposterior que tenha sido publicada (não como rascunho) pela Free Software Foundation. Se oDocumento não especificar um número de Versão desta Licença, você pode escolher qualquerversão já publicada (não como rascunho) pela Free Software Foundation.

ADENDO: Como usar esta Licença para seus documentos

Para usar esta Licença num documento que você escreveu, inclua uma cópia desta Licençano documento e ponha as seguintes notas de copyright e licenças logo após a página de título:

Copyright (c) ANO SEU NOME.É dada permissão para copiar, distribuir e/ou modificar este documento sob os termos da Licençade Documentação Livre GNU, Versão 1.1 ou qualquer versão posterior publicada pela Free Soft-ware Foundation; com as Seções Invariantes sendo LISTE SEUS TÍTULOS, com os Textos daCapa da Frente sendo LISTE, e com os Textos da Quarta-Capa sendo LISTE. Uma cópia da li-cença está inclusa na seção entitulada "Licença de Documentação Livre GNU".

Se você não tiver nenhuma Seção Invariante, escreva "sem Seções Invariantes"ao invés dedizer quais são invariantes. Se você não tiver Textos de Capa da Frente, escreva "sem Textos deCapa da Frente"ao invés de "com os Textos de Capa da Frente sendo LISTE"; o mesmo para osTextos da Quarta Capa.

Se o seu documento contiver exemplos não triviais de código de programas, nós recomenda-mos a publicação desses exemplos em paralelo sob a sua escolha de licença de software livre,

17

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

tal como a GNU General Public License, para permitir o seu uso em software livre.

18

Parte IV

Driver de Dsipositivo Linux

19

Capítulo 1

Driver de Dispositivo Linux

Drivers de dispositivo são a interface necessária entre o sistema operacional e o hardware do seucomputador. Drivers são responsáveis por controlar de forma correta os dispositivos e esconderdo próprio sistema operacional os detalhes de baixo nível.

20

Capítulo 2

Plano de ensino

2.1 Objetivo

Capacitar o usuário para o desenvolviemento de drivers de dispositivo para o Kernel 2.6.x doSO GNU/Linux.

2.2 Público Alvo

Técnicos e Programadores com experiência em programação usando C que desejam umcaminho para criar os próprios drivers de dispositivo.

2.3 Pré-requisitos

É necessário sólido conhecimento em lógica de programação e familiaridade com a linguagemC, além de domínio do SO GNU/Linux.

2.4 Descrição

O curso de Driver de Dispositivo Linux será realizado na modalidade EAD e utilizará a plata-forma Moodle como ferramenta de aprendizagem. O material didático estará disponível on-linede acordo com as datas pré-estabelecidas no calendário. A versão utilizada para o Kernel doLinux será a 2.6.18; O curso está dividido da seguinte maneira:

2.5 Cronograma

• Introdução;

• Interface SO<->driver.

• Código de exemplo.

• Estruturas do dispositivo.

• Aprimoração do driver exemplo

21

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

• Alocação e manipulação de memória

• Finalização do driver exemplo

As lições contém o contéudo principal. Elas poderão ser acessadas quantas vezes forem neces-sárias, desde que esteja dentro da semana programada. Ao final de uma lição, você receberáuma nota de acordo com o seu desempenho. Responda com atenção às perguntas de cada lição,pois elas serão consideradas na sua nota final. Caso sua nota numa determinada lição for menordo que 6.0, sugerimos que você faça novamente esta lição.

Ao final do curso será disponibilizada a avaliação referente ao curso. Tanto as notas das liçõesquanto a da avaliação serão consideradas para a nota final. Todos os módulos ficarão visíveispara que possam ser consultados durante a avaliação final.

Aconselhamos a leitura da "Ambientação do Moodle"para que você conheça a plataforma deEnsino a Distância, evitando dificuldades advindas do "desconhecimento"sobre a mesma.

Os instrutores estarão a sua disposição ao longo de todo curso. Qualquer dúvida deverá serenviada no fórum. Diariamente os monitores darão respostas e esclarecimentos.

2.6 Programa

O curso de Driver de Dispositivo Linux oferecerá o seguinte conteúdo:

• Estudo das estruturas do kernel

• Estudo das áreas de memória

• Estudo da criação de arquivos de dispositivo

• Programação em C dos drivers de exemplo

2.7 Avaliação

Toda a avaliação será feita on-line.Aspectos a serem considerados na avaliação:

• Iniciativa e autonomia no processo de aprendizagem e de produção de conhecimento;

• Capacidade de pesquisa e abordagem criativa na solução dos problemas apresentados.

Instrumentos de avaliação:

• Participação ativa nas atividades programadas.

• Avaliação ao final do curso.

• O participante fará várias avaliações referente ao conteúdo do curso. Para a aprovação eobtenção do certificado o participante deverá obter nota final maior ou igual a 6.0 de acordocom a fórmula abaixo:

• Nota Final = ((ML x 7) + (AF x 3)) / 10 = Média aritmética das lições

• AF = Avaliações

22

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

2.8 Bibliografia

• The Linux Kernel: http://www.kernel.org

• LinuxDevCenter: http://www.linuxdevcenter.com

23

Capítulo 3

Introdução

O que são drivers de dispositivo e como eles funcionam? Quais os tipos de driver de disposi-tivo? Como funciona a interação do sistema operacional com o driver?

3.1 Driver de Dispositivo?

Quando um programa precisa fazer um acesso a sua impressora para imprimir qualquer coisa,é utilizado um device driver. Sem um desses, o programa necessitaria de acesso direto a im-pressora e, pior ainda, teria que saber exatamente como o controlador físico dessa impressorafunciona. Isso provavelmente ficaria ainda pior: todo programa que fosse imprimir funcionaria so-mente para uma impressora ou teria que saber como funcionam todas as impressoras do mundo,além de saber como identificá-las. Certamente, isso é inviável.

Device drivers fornecem um nível de abstração para o desenvolvimento de programas quenecessitam acesso a um dispositivo físico. Isso quer dizer que, ao invés de embutir em todosos programas o código para controlar a impressora X, simplesmente chamo uma função (porexemplo, print()) em meu programa e o sistema operacional se encarrega de procurar nos driverscarregados o código correspondente, e delega a essa porção de código a responsabilidade pelocontrole da impressora. Se amanhã eu decidir que preciso de uma impressora Y e não mais daX, basta instalar os drivers da impressora Y e nada mais precisará de alteração.

Assim funciona com basicamente tudo: discos, mouses, teclados, placas de vídeo, etc. Ge-ralmente você precisa identificá-los somente uma vez, e o sistema operacional se encarrega deencontrar os drivers corretos e esse sim é responsável pelo controle adequado do hardware.

Na maioria dos sistemas operacionais, os device drivers mais importantes (também os maisgenéricos) e utilizados em grande escala pelo computador geralmente estão compilados dentrodo kernel (núcleo do sistema), e os utilizados com menos freqüência ou que não exigem demandamuito grande são carregados como módulos. Também são módulos os drivers que não estãodisponíveis inicialmente pelo sistema operacional - drivers disponibilizados por terceiros ou osque você criar.

Drivers devem ser programados e tratados cuidadosamente: um código destinado a ser driverde dispositivo funcionará com permissão ao hardware e ao sistema em si, podendo danificá-losseriamente.

A maior parte do curso será abordada dentro do sistema operacional GNU/Linux, pois, pelacaracterística de ser código aberto, possui a transparência necessária para o processo de criaçãode device drivers.

24

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

3.2 Tipos de Dispositivos

Basicamente os dispositivos de hardware são separados da seguinte forma: dispositivos decaractere e dispositivos de bloco (Character device e Block device, respectivamente; há aindaos dispositivos de rede que possuem características diferentes e não serão abordados por en-quanto). A diferença entre eles é que o acesso aos dados de um dispositivo de caractere éseqüencial, enquanto nos dispositivos de bloco o acesso geralmente é aleatório.

Isso é, um dispositivo de caractere é tão rápido quanto possível para escrever e ler dadosdo seu hardware e portanto não precisa de um buffer (o dispositivo em si não trabalha com umbuffer). Uma outra característica de um dispositivo seqüencial é que não é possível ’retroceder’ aposição dos dados (na verdade, não faz o menor sentido). São dispositivos de streamming. Umbom exemplo de um dispositivo de caractere é o teclado: não faz sentido, via hardware, "ler a teclaque foi digitada 10 teclas atrás", mas no entanto cada tecla pressionada é imediatamente enviadaao sistema operacional. Dispositivos de caracteres também têm esse nome pois geralmente oacesso é feito "um caractere (1 byte)"por vez.

Já num dispositivo de bloco, por sua característica de acesso aleatório, é imprescindível o usode um buffer. Na verdade, enquanto um programa utiliza um dispositivo de bloco, ele pode atéter a impressão que está lendo e escrevendo diretamente no dispositivo (como faria normalmenteem um dispositivo de caractere), mas, na verdade, está realizando tais operações no buffer dodispositivo e esse, por sua vez, se encarrega de fazer a mudança fisicamente, na hora certa.Analisando o seu disco rígido é fácil chegar a conclusão de que ele é um dispositivo de bloco,pois você pode acessar dados que estão em partes diferentes do disco. Se o acesso ao discofosse seqüencial, essa operação seria simplesmente impossível. Uma outra razão para que sejachamado dispositivo de blocos é que o acesso ao dispositivo é feito em blocos de dados (512bytes, por exemplo).

No entanto, para a maioria dos usuários do Linux/Unix, não faz diferença se o dispositivo é debloco ou de caractere: é possível ler e escrever nesse dispositivo como se ele fosse um arquivoqualquer. A distinção de que tipo de dispositivo e como ler e escrever nele fica por conta dosistema operacional e do driver.

3.3 Interface do Driver com o Sistema Operacional

mesma forma que o sistema operacional cria a possibilidade de um programa imprimir utili-zando a simples função print(), um device driver deve possibilitar o sistema operacional controlaressa mesma impressora utilizando uma função como control_printer(), independente da impres-sora. Isso quer dizer que o SO não se importa se você vai usar uma impressora da marca X ouY, ele simplesmente vai chamar a função control_printer(), e o seu device driver deve responder aessa chamada de função da mesma forma que o sistema operacional responde à função print() doprograma. Em outros termos, enquanto o device driver é a implementação virtual da impressora,a função control_printer() é a sua interface com o sistema operacional. Para dar mais um realceà idéia de interface e implementação: se hoje você utiliza discos SCSI e amanhã decidir utilizardiscos IDE, a única mudança é o driver que será carregado. O Sistema operacional continuaráchamando as mesmas funções de acesso ao disco.

Sendo um pouco mais exato, cada device driver deve implementar uma struct cujos seuselementos são os ponteiros para as funções de controle, e o kernel, quando carrega esse driver,registra essa struct na memória. Assim que o driver é efetivamente carregado, o kernel chama afunção de inicialização. O trabalho da função de inicialização consiste em registrar o dispositivo

25

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

na tabela de dispositivos e dar a ele a identificação necessária, além de inicializar outras structsque o driver vai utilizar. Na utilização do dispositivo, o kernel chama as funções de controle,leitura e escrita disponibilizadas. Quando o driver é descarregado, o kernel chama a função definalização, responsável por desregistrar o dispositivo no sistema e limpar da memória. Vamosver tudo isso com mais detalhes, pode ficar tranqüilo :)

26

Capítulo 4

Hello Kernel!

Um Driver simples e que não faz nada!

4.1 Kello Hernell!

Para exemplificar a interface do sistema operacional com o driver, vamos criar um "Hello Ker-nel". O driver não faz nada de muito importante, só exibe uma mensagem quando é carregado eoutra quando é descarregado./*----------------------- ut here ---------------------------- */#in lude <linux/init.h>#in lude <linux/module.h>MODULE\_LICENSE("Dual BSD/GPL") /* Evita re lama oes de kernel */stati int hellok(void) {printk(KERN\_ALERT "Kello, Hernel! :℄\n");/* Coisas que fazem os drivers de dispositivos */return 0;}stati void byek(void) {printk(KERN\_ALERT "Bye, Kernel! :[\n");}module\_init(hellok);module\_exit(byek);/*----------------------- ut here ---------------------------- */4.2 Como funciona?

Dissecando o nosso módulo (que não tem muito o que ser dissecado, na verdade), encontra-mos algumas coisas parecidas com programas normais, outras, nem tanto. A primeira coisa a sernotada são os arquivos de include: nada de stdio.h nem string.h. Entretanto, temos <linux/init.h>e <linux/module.h>. Esses dois cabeçalhos devem aparecer em todos os módulos carregáveis.<linux/module.h> tem muitas definições importantes e símbolos utilizados em módulos e init.h

27

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

disponibiliza module_init e module_exit, além de outras coisas. Drivers geralmente utilizam muitomais includes, mas esses você vai encontrar em qualquer um deles.

Você provavelmente deve ter percebido que não temos uma função main(). Ao invés delaexistem as chamadas macros module_init e module_exit, que recebe como parâmetros, respec-tivamente, as funções de inicialização e de saída do módulo. Quando o driver for carregado,ele chama a função registrada por module_init e quando descarregar, a função registrada emmodule_exit.

Para imprimir na tela, temos uma função que engana. Parece muito a velha conhecida printf.No entanto, printk é uma função controlada unicamente pelo kernel, pois esse não pode de-pender de funções disponibilizadas por bibliotecas que podem não estar presentes no futuro (seutilizássemos printf, nosso módulo dependeria da presença da libc, que muito provavelmente nãoestará disponível). Outras características que diferem printk de printf (além de trocar f por k): oargumento de urgência (KERN_ALERT, que é definida como <1>. Quanto maior o número entre<>, menor a prioridade. Dependendo do valor, a mensagem pode nem mesmo ser exibida) quevem antes da string que será exibida. Outra é que printk não suporta números de ponto flutuante(float).

A diferença mais importante de um módulo para um programa comum é que módulos sãoorientados ao evento. Em outras palavras, enquanto o seu programa favorito abre, executa o quetem que executar e depois sai, um módulo é carregado no kernel informando o que sabe fazer ecom quem. Depois disso não faz mais nada. Fica lá, parado, junto com um monte de módulos,esperando ordens que podem aparecer ou não.

Para compilar esse módulo é necessário um pouco mais de trabalho. Primeiramente, parakernels da versão 2.6.x, você vai precisar dos fontes do kernel instalados e compilados. Emversões 2.4.x, apenas os headers do kernel eram necessários.

Vai ser necessário criar um makefile um pouco diferente dos padrões para que a compilaçãofuncione. O kernel tem seu próprio ’jeitinho’ de utilizar makefiles, chamado kbuild.

Por hora, nosso Makefile vai se parecer com isso# ----------------------- ut here ----------------------------obj-m := hellokernel.o# ----------------------- ut here ----------------------------Esse makefile deve estar no mesmo diretório do código do seu módulo. Compile-o da seguinte

forma:make -C /caminho/do/fonte/linux M=‘pwd‘ modules(Digite esse comando dentro da pasta que está o seu makefile e o seu módulo) No final, você

vai ter um arquivo do formato hellokernel.ko.Carregue o módulo utilizando o comando insmod hellokernel.ko, e o descarregue com rmmod

hellokernel.ko. Se tudo correu bem, o driver vai exibir as respectivas mensagens que programa-mos.

28

Capítulo 5

Arquivos de Dispositivo

Acesso aos dispositivos via os arquivos do /dev. Arquivos de dispositivos? Como assim?

5.1 Tudo é um arquivo!

"No Linux, tudo é um arquivo". Essa é a máxima da maioria dos usuários do Linux. Enquantooutros sistemas operacionais mais populares representam dispositivos de armazenamento porletras e "escondem"a localização dos outros dispositivos (e como eles são controlados), no sis-tema operacional do pingüim todos os dispositivos tem uma correspondência no diretório /dev(com exceção das interfaces de rede), onde são, de fato, arquivos. Sendo assim, você podetestar o comando _ echo "Oi dispositivo!» /dev/tty1 _ e dependendo de onde você utilizar essecomando, aparecerá na sua tela a mensagem "Oi dispositivo!"(/dev/tty* são dispositivos de termi-nais). Quem provê esse tipo de praticidade é em parte o sistema operacional e em parte o seudriver de dispositivo. Quando você chama o comando "echo"com redirecionamento de saída (>)para um arquivo de dispositivo, o sistema operacional logo descobre qual é o driver que o controlae envia a mensagem a ele.

Talvez você se pergunte agora: "como o sistema operacional vai descobrir qual driver controlaaquele dispositivo?". Ou, pior, como o SO sabe que tipo de arquivo é esse? Por que é umdispositivo? Se você já utilizou alguma vez o comando mknod, deve lembrar-se que ele cria arepresentação (em forma de arquivo) para os dispositivos.

Analisemos o comando com um exemplo: # mknod /dev/meudispositivo 60 0O primeiro parâmetro obviamente é o nome do arquivo que representará o dispositivo. O

parâmetro c diz que está sendo criado um dispositivo de caractere. O que nos interessa agorasão os dois números subseqüentes, 60 e 0. O 60 seria o "major number"e 0 o "minor number".Major/minor numbers são utilizados como a identidade virtual do hardware. Junto com o tipo dedispositivo (no nosso caso, um dispositivo de caractere indicado com o parâmetro ’c’) e com essesnúmeros, o sistema operacional é capaz de identificar qual driver está controlando o dispositivoe faz a ligação entre eles dois. Vários dispositivos podem utilizar o mesmo major number (indi-cando assim que são controlados pelo mesmo driver), mas cada um deles tem um minor numberúnico. Sendo um pouco mais claro, major numbers identificam para o sistema operacional, qualdispositivo controla aquele hardware e minor numbers identificam para o driver qual a variaçãoque aquele hardware possui, para que esse possa adequar-se. Eu sei que vou acabar repetindoisso em algum ponto do curso. Se você não entendeu muito bem, continue lendo. Lembre-seque os números de dispositivo indicam apenas qual driver está controlando aquele hardware, enão o seu tipo. Isso significa que um mesmo driver pode controlar todos os tipos de dispositivos

29

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

do mundo: vai saber diferenciá-los pelo minor number. Obviamente que isso é uma tática semsentido (para não dizer burra). O módulo seria um bloco gigantesco de código e muito lento, semcontar que, se eu preciso controlar as LEDs do meu gabinete, é praticamente uma piada carregaros controladores de um mouse.

O driver por sua vez, quando é carregado na memória deve passar o major number do dis-positivo e quantos minor numbers ele pretende controlar, e o kernel registrará na sua tabela dedispositivos aquele driver. Dessa forma, o SO "casa"o arquivo de dispositivo com o seu driver. Otcl é muito útil para quem quer manipular string, ele oferece uma vasta quantidade de comandospara esse fim, como nosso curso é apenas uma introdução à linguagem não abordaremos todosos comandos.

5.2 Major e Minor Numbers

A representação de major e minor numbers dentro do kernel fica por conta de um tipo de dadodeclarado em linux/types.h. É o tipo dev_t que, na verdade, é um espaço de 32 bits onde 12 bitssão reservados para major numbers e os 20 bits restantes são reservados para minor numbers.As macros/funções que trabalham com o tipo dev_t estão no cabeçalho linux/kdev_t.h. As maisimportantes para o esse momento no curso são MAJOR, MINOR e MKDEV.

MAJOR e MINOR retornam major e minor numbers, respectivamente, de uma dada variáveldo tipo dev_t. MKDEV faz o contrário. Sabendo o major e o minor number, ele constrói a estruturae a retorna no formato de dev_t.

MAJOR(dev_t dev); MINOR(dev_t dev); MKDEV(int major, int minor);Ex: dev_t dev = MKDEV(60, 0); int major = MAJOR(dev); int minor = MINOR(dev);Como dito anteriormente, cada device driver deve declarar major e minor numbers que vai

controlar. Existem duas formas de fazer isso, com funções prototipadas em linux/fs.h. A primeiraé usar a função register_chrdev_region para drivers de caractere, que tem essa cara:

int register_chrdev_region(dev_t first, unsigned int count, char *name);first é o nosso número de dispositivo (que pode ter sido criado com MKDEV, por exemplo).

Geralmente, mas não necessariamente, o minor number em first é 0. count é a quantidade dedevices que o driver controla; isso é, quantos minor numbers ele vai utilizar. name é o nome dodispositivo que vai aparecer em /proc/devices. Não confunda isso com o nome em /dev. Quemcontrola isso é o comando mknod. A função retorna 0 se tudo correr bem, e qualquer outronúmero se houver algum erro. A desvantagem dessa função é que o controle sobre os númerosé precário: você pode pedir números já alocados. Para contornar essa situação, existe umafunção de alocação dinâmica de números de dispositivos. A função tem esse protótipo:

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);dev deve ser um ponteiro do tipo dev_t, onde será armazenado o resultado da alocação dos

números de dispositivo. firstminor é o primeiro minor a ser utilizado. Os outros parâmetros sãoiguais aos de register_chrdev_region. A grande utilidade dessa função é que a probabilidadede colisão de números é pouca, pois retorna o primeiro número de dispositivo não utilizado.No entanto, isso torna mais difícil a utilização do comando mknod, pois você deve buscar em/proc/devices o número para utilizar.

Uma dessas funções (register_chrdev_region ou alloc_chrdev_region) deve estar na sua fun-ção de inicialização. Quando o device driver for descarregado, a função unregister_chrdev_regiondeve estar presente na função de finalização.

unregister_chrdev_region tem esse protótipo:void unregister_chrdev_region(dev_t first, unsigned int count);

30

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

Os parâmetros first e count são os mesmos de register_chrdev_region. Vale lembrar queessas funções são para drivers de caractere. As funções de driver de bloco têm um jeito muitoparecido.

Já dá para deixar nosso módulo um pouquinho mais interessante.kernumbers.c/*----------------------- ut here ---------------------------- */#in lude <linux/init.h>#in lude <linux/module.h>#in lude <linux/types.h>#in lude <linux/kdev\_t.h>#in lude <linux/fs.h>MODULE\_LICENSE("Dual BSD/GPL") /* Evita re lama oes de kernel *//* Variáveis globais a esse arquivo. Podemos pre isar delas mais tarde */stati int major;stati dev\_t dev;stati int hellok(void) {printk(KERN\_ALERT "Kello, Hernel! :℄\n");/* Reigstra um driver om 3 minor numbers om o nome "driver" */if (!allo \_ hrdev\_region(&dev, 0, 3, "driver")) {printk(KERN\_ALERT "Houve um problema alo ando os números de driver\n");exit(1);}major = MAJOR(dev);printk(KERN\_ALERT "Nosso major number é %d\n", major);/* Outras oisas que fazem os drivers de dispositivos */return 0;}stati void byek(void) {printk(KERN\_ALERT "Bye, Kernel! :[\n");/* Desregistra o nosso driver */unregister\_ hrdev\_region(&dev, 3);}module\_init(hellok);module\_exit(byek);/*----------------------- ut here ---------------------------- */

O código é auto-explicativo. O Makefile deve ser alterado de acordo com o novo nome domódulo. Para verificar se está tudo ok, você pode dar uma olhada no arquivo /proc/devices e/proc/modules: # at /pro /modules | grep kernumbers e # at /pro /devi es | grep driverO primeiro deve mostrar o seu módulo na lista de módulos carregados e o segundo mostra o nú-mero utilizado pelo seu "dispositivo".

31

Capítulo 6

Estruturas

As estruturas mais importantes para um driver de dispositivo: filp, fops e inode.

6.1 File Operations

6.1.1 Introdução

Até agora foi a parte fácil da implementação de um driver de dispositivo. Se você queria saberapenas como funcionam os módulos, pode parar por aqui, pois a partir de agora a gente entrana parte em que o curso fica interessante (e complicado). Vamos levar o nosso driver um poucomais a sério: implementaremos a estrutura de operações de arquivo. No entanto, o nosso drivervai fazer todas as operações num dispositivo de caractere "fictício".

Como já dito, quando o driver é carregado na memória, além de registrar major e minor num-bers, ele deve registrar a estrutura de operações de arquivo referente ao driver (lembre-se quemesmo os dispositivos são representados como arquivos no linux e por isso precisam declararquais operações estão disponíveis para esse arquivo). A definição dessas estruturas estão em<linux/fs.h>.

Mesmo que o parágrafo acima não tenha ficado muito claro, tudo fica mais óbvio quandotemos um pedaço de código para analisar, certo? Enfim chega de bate-papo. Vamos dar umaboa olhada na estrutura de operações de arquivo. É uma das mais importantes.

6.1.2 Fops

Esta é a famosa "fops (file operations)":stru t file\_operations {stru t module *owner;loff\_t (*llseek) (stru t file *, loff\_t, int);ssize\_t (*read) (stru t file *, har \_\_user *, size\_t, loff\_t *);ssize\_t (*write) (stru t file *, onst har \_\_user *, size\_t, loff\_t *);ssize\_t (*aio\_read) (stru t kio b *, onst stru t iove *, unsigned long, loff\_t);ssize\_t (*aio\_write) (stru t kio b *, onst stru t iove *, unsigned long, loff\_t);int (*readdir) (stru t file *, void *, filldir\_t);unsigned int (*poll) (stru t file *, stru t poll\_table\_stru t *);int (*io tl) (stru t inode *, stru t file *, unsigned int, unsigned long);long (*unlo ked\_io tl) (stru t file *, unsigned int, unsigned long);32

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFlong (* ompat\_io tl) (stru t file *, unsigned int, unsigned long);int (*mmap) (stru t file *, stru t vm\_area\_stru t *);int (*open) (stru t inode *, stru t file *);int (*flush) (stru t file *, fl\_owner\_t id);int (*release) (stru t inode *, stru t file *);int (*fsyn ) (stru t file *, stru t dentry *, int datasyn );int (*aio\_fsyn ) (stru t kio b *, int datasyn );int (*fasyn ) (int, stru t file *, int);int (*lo k) (stru t file *, int, stru t file\_lo k *);ssize\_t (*sendpage) (stru t file *, stru t page *, int, size\_t, loff\_t *, int);unsigned long (*get\_unmapped\_area)(stru t file *, unsigned long, unsigned long, \unsigned long, unsigned long);int (* he k\_flags)(int);int (*dir\_notify)(stru t file *filp, unsigned long arg);int (*flo k) (stru t file *, int, stru t file\_lo k *);ssize\_t (*spli e\_write)(stru t pipe\_inode\_info *, stru t file *, loff\_t *, size\_t, unsigned int);ssize\_t (*spli e\_read)(stru t file *, loff\_t *, stru t pipe\_inode\_info *, size\_t, unsigned int);int (*setlease)(stru t file *, long, stru t file\_lo k **);};É uma estrutura um tanto quanto densa. A maioria do seu conteúdo são ponteiros para funçõesque você deve implementar. A estrutura em si é apenas a interface. Preencher essa struct éuma das operações que seu driver deve fazer na inicialização. Para que serve cada uma dessasfunções? Você pergunta. Bom, algumas dão para deduzir pelo nome, outras, nem tanto. Dequalquer forma, vamos analisar os elementos mais importantes para o nosso driver.

struct module *owner: O primeiro ponteiro não é uma função. É um ponteiro para o mó-dulo que é o "dono"dessa struct. Geralmente, o seu valor é THIS_MODULE. (THIS_MODULE édefinido em <linux/module.h>).

loff_t (*llseek) (struct file *, loff_t, int): Função responsável pela manipulação da cabeça deleitura (offset) do arquivo. Esse offset é declarado na estrutura de arquivo, que veremos adiante.Ela retorna a nova posição no arquivo, se for bem sucedida ou algum número negativo caso falhe.

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *): O nome denuncia. É usado paraler informações do dispositivo. Retorna a quantidade que foi lida com sucesso ou um númeronegativo caso falhe. Se esse ponteiro for declarado como NULL, o arquivo não poderá ser lido.

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *): Escreve dados em umdispositivo. Retorna a quantidade que foi escrita com sucesso ou um número negativo caso falhe.Se esse ponteiro for declarado como NULL, a escrita no arquivo de dispositivo será impossível.

ssize_t (*aio_read) (struct kiocb *, const struct iovec *, u nsigned long, loff_t) e ssize_t(*aio_write) (struct kiocb *, const struct iovec *, unsigne d long, loff_t): Fazem a mesma coisaque read e write, mas de forma assíncrona, isto é, a função pode retornar antes que a operaçãotermine. Se forem NULL, todas as operaçõe de leitura e escrita serão feitas de forma síncrona.

int (*readdir) (struct file *, void *, filldir_t): Função que permite a leitura de diretórios. Paradriver de dispositivo ela não é implementada.

unsigned int (*poll) (struct file *, struct poll_table_stru ct *): Função que retorna uma más-cara informando se é possível ou não utilizar as funções de leitura/escrita sem que elas "blo-queiem"o fluxo do programa (por exemplo, scanf é uma função que interrompe o programa).

int (*ioctl) (struct inode *, struct file *, unsigned int, uns igned long): Função utilizadapara mandar comandos especfíficos do hardware em questão, que não são nem de leitura nem

33

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

escrita. geralmente são comandos de controle de dispositivo.long (*unlocked_ioctl) (struct file *, unsigned int, unsign ed long) e long (*compat_ioctl)

(struct file *, unsigned int, unsigned long): São alternativas mais recentes à ioctl(). ioctl()utiliza um recurso chamado Big Kernel Lock (BKL). BKLs são utilizadas em sistemas multiproces-sados (SMP) para garantir a consistência do kernel. Existem partes críticas do kernel que, paracontinuarem funcionando, devem esperar a resposta de algum dispositivo que foi controlado comioctl() (como por exemplo, o resultado de um cálculo executado por outro processador na mesmamáquina). Assim, antes de se utilizar alguma ioctl() crítica, o kernel deve ser colocado em modode espera. unlocked_ioctl() é um recurso relativamente recente no kernel (apareceu por volta daversão 2.6.11 em forma de um patch) que deve implementar dentro de seu próprio código umaforma de bloquear a parte significativa para a chamada do kernel. compat_ioctl serve para fazerchamadas de 32 bits em sistemas de 64 bits. As duas funções não serão abordadas nesse curso,por serem de uso muito específico. (Por curiosidade, ioctl() não é a única função que utiliza BKL.open(), llseek() e outras também utilizam).

int (*mmap) (struct file *, struct vm_area_struct *): mmap() é muito importante pois tornadisponível alguma área de memória do dispositivo ao processo chamador, sendo possível controlá-la.

int (*open) (struct inode *, struct file *): Função sempre chamada quando um arquivo dedispositivo (/dev/hda1, por exemplo) é aberto. O seu driver não precisa implementá-la e a aberturasempre irá funcionar. No entanto, sem essa chamada no seu driver, é impossível saber quando oarquivo de dispositivo está sendo aberto.

int (*flush) (struct file *, fl_owner_t id): Dispositivos que trabalham com buffer geralmenteaguardam o preenchimento de um certo tamanho de bloco na região da sua memória antesde efetuar a operação de escrita. flush() certifica-se que essa operação será executada. Essafunção é chamanda somente antes da aplicação encerrar as operações com a sua cópia dodescritor do arquivo (file descriptor), e portanto não deve ser confundida com fsync(). Observeque o programador geralmente não tem controle sobre flush().

int (*release) (struct inode *, struct file *): Chamada quando o arquivo é fechado. Assimcomo open(), se for NULL, o seu driver não será avisado dessa operação. Observe que release sóé chamado pelo SO quando todos os aplicativos encerram sua utilização do o dispositivo. Assim,se dois processos complartilham o mesmo descritor de arquivo (utilizando fork(), por exemplo),somente flush() é utilizado quando um deles é encerrado. Se ninguém mais utiliza o arquivo, aísim é chamado release().

int (*fsync) (struct file *, struct dentry *, int datasync): Essa operação se encarrega detratar qualquer dado ainda pendente entre o processo e o dispositivo. É como flush(), mas oprogramador pode utilizá-la em qualquer momento.

int (*aio_fsync) (struct kiocb *, int datasync): Como fsync, mas é assíncrona. Isso é, nãodeixa o processo chamador em modo de espera.

int (*fasync) (int, struct file *, int): Utilizado para notificar ao dispositivo alguma mudança naflag FASYNC. Não será abordado nesse curso.

int (*lock) (struct file *, int, struct file_lock *): Implementa a "trava"de arquivo, isto é, se umprocesso está "escrevendo"no arquivo, outro processo deve esperar a liberação antes de efetuaralguma outra operação que precise de lock.

ssize_t (*sendpage) (struct file *, struct page *, int, size_ t, loff_t *, int); unsigned long(*get_unmapped_area)(struct file *, unsigned long, unsign ed long, unsigned long, unsig-ned long); int (*check_flags)(int); int (*dir_notify)(str uct file *filp, unsigned long arg); int(*flock) (struct file *, int, struct file_lock *); ssize_t (*sp lice_write)(struct pipe_inode_info *,struct file *, loff_t *, size_t, unsigned int); ssize_t (*spl ice_read)(struct file *, loff_t *, struct

34

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

pipe_inode_info *, size_t, unsigned int); int (*setlease) (struct file *, long, struct file_lock **);As funções acima sem explicação tratam de tópicos mais avançados, fora do escopo desse

curso, ou são funções de pouco uso em device drivers. Lembre-se que as funções nessa estruturanão são específicas de drivers de dispositivos, mas pertencem à struct file, que descreve qualquerarquivo carregado na memória e/ou controlado pelo kernel. Mesmo dentre as funções explicadas,existem algumas que jamais serão implementadas por nós, como aio_fsync ou unlocked_ioctl.

6.1.3 Inicializando a fops

Há uma coisa que tem que estar clara em sua cabeça, e que talvez ainda não esteja clarano curso. Perceba que a estrutura file_operations pertence AO KERNEL (mais especificamente,ao nosso driver). Um programador jamais irá chamar diretamente write() ou read() do dispositivo,por exemplo. Um usuário do sistema muito menos. Lembra do "echo Oi! > /dev/tty1"? Nessemomento, o shell abre o arquivo /dev/tty1 com open() (da biblioteca fcntl.h) e o kernel percebea chamada e utiliza open() do driver. Então o shell utiliza write() e assim também faz o kernel.Quando o shell encerra a utilização do arquivo, ele chama close() e o kernel usa flush() e release().Portanto, os módulos devem implementar essas chamadas e inicializá-las na estrutura de fileoperations. Vamos desenvolver um pouco mais o nosso driver:

mydev.c/*----------------------- ut here ---------------------------- */#in lude <linux/init.h>#in lude <linux/module.h>#in lude <linux/types.h>#in lude <linux/kdev\_t.h>#in lude <linux/fs.h>MODULE\_LICENSE("Dual BSD/GPL");stati int major;stati dev\_t dev;/* Prototipos das fun oes prin ipais */int mydev\_open(stru t inode * inode, stru t file * filp);int mydev\_release(stru t inode * inode, stru t file * filp);ssize\_t mydev\_read(stru t file * filp, har \_\_user * buf, size\_t ount, loff\_t * f\_pos);ssize\_t mydev\_write(stru t file * filp, onst har \_\_user * buf, size\_t ount, loff\_t * f\_pos);stati int mydev\_init(void);stati void mydev\_exit(void);/* Ini ializa ao da fops */stru t file\_operations mydev = {.owner = THIS\_MODULE,.open = mydev\_open,.release = mydev\_release,.read = mydev\_read,.write = mydev\_write,};stati int mydev\_init(void) {35

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFprintk(KERN\_ALERT "Carregando mydev\n");/* registra o driver om 3 minor numbers */if (allo \_ hrdev\_region(&dev, 0, 0, "driver")) {printk(KERN\_ALERT "Houve um problema alo ando o driver\n");return 1;}major = MAJOR(dev);printk(KERN\_ALERT "Nosso major number e %d\n", major);return 0;}stati void mydev\_exit(void) {printk(KERN\_ALERT "Des arregando modulo\n");unregister\_ hrdev\_region(dev, 0);}module\_init(mydev\_init);module\_exit(mydev\_exit);/*----------------------- ut here ---------------------------- */A primeira diferença é que o módulo está com uma carinha mais séria. Uma outra é que

prototipamos as nossas funções básicas. Depois, criamos uma struct file_operations, chamadamydev, e inicializamos com aquelas funções prototipadas. O resto do código certamente você jáconhece.

Agora você me pergunta: "É possível usar esse módulo só com o protótipo das funções?".Provavelmente, se está me fazendo essa pergunta, você não compilou e carregou o módulo. Aresposta é NÃO. A princípio, é possível compilar com sucesso esse módulo, mas a compilaçãovai dar avisos de símbolos indefinidos. Bom, ignorando isso, tentando um insmod mydev.ko, omódulo não carrega, alegando agora símbolos desconhecidos.

6.1.4 Algumas operações de mentirinha

Já que o kernel está reclamando que não conhece alguns símbolos, vamos criá-los. Segue ocódigo, dessa vez com uma implementação./*----------------------- ut here ---------------------------- */#in lude <linux/init.h>#in lude <linux/module.h>#in lude <linux/types.h>#in lude <linux/kdev\_t.h>#in lude <linux/fs.h>MODULE\_LICENSE("Dual BSD/GPL"); /* Sai pra lá, kernel hato! */stati int major;stati dev\_t dev;/* Prototipos das fun oes prin ipais */int mydev\_open(stru t inode * inode, stru t file * filp);int mydev\_release(stru t inode * inode, stru t file * filp);

36

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFssize\_t mydev\_read(stru t file * filp, har \_\_user * buf, size\_t ount, loff\_t * f\_pos);ssize\_t mydev\_write(stru t file * filp, onst har \_\_user * buf, size\_t ount, loff\_t * f\_pos);stati int mydev\_init(void);stati void mydev\_exit(void);/* Ini ializa ao da fops */stru t file\_operations mydev\_fops = {.owner = THIS\_MODULE,.open = mydev\_open,.release = mydev\_release,.read = mydev\_read,.write = mydev\_write,};stati int mydev\_init(void) {printk(KERN\_ALERT "Carregando mydev\n");/* registra o driver om 3 minor numbers */if (allo \_ hrdev\_region(&dev, 0, 3, "driver")) {printk(KERN\_ALERT "Houve um problema alo ando o driver\n");return 1;}major = MAJOR(dev);printk(KERN\_ALERT "Nosso major number e %d\n", major);return 0;}stati void mydev\_exit(void) {printk(KERN\_ALERT "Des arregando driver\n");unregister\_ hrdev\_region(dev, 3);}int mydev\_open(stru t inode * inode, stru t file * filp) {printk(KERN\_ALERT "mydev\_open()\n");return 0;}int mydev\_release(stru t inode * inode, stru t file * filp) {printk(KERN\_ALERT "mydev\_release()\n");return 0;}ssize\_t mydev\_write(stru t file * filp, onst har \_\_user * buf,size\_t ount, loff\_t * f\_pos) {printk(KERN\_ALERT "mydev\_write()\n");return ount;}ssize\_t mydev\_read(stru t file * filp, har \_\_user * buf, size\_t ount, loff\_t * f\_pos) {37

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFprintk(KERN\_ALERT "mydev\_read()\n");return ount;}module\_init(mydev\_init);module\_exit(mydev\_exit);/*----------------------- ut here ---------------------------- */Pronto, agora o compilador não reclama mais. Nem o insmod. O problema é que nosso

driver ainda não faz absolutamente nada. Se você tentar ler ou escrever nele, provavelmenteo programa que você utilizar vai retornar algum erro do tipo "endereço de dispositivo inválido".Ainda está faltando uma peça importante. Siga lendo e você vai descobrir o que é.

6.2 Filp

Durante o curso, vamos chamar de filp um ponteiro para a struct file, só para facilitar a refe-rência (e o nome filp também é comumente utilizado pelos programadores de kernel).

Vamos dar uma olhada no que é de mais pertinente para nós.unsigned int f_flags: Indica flags do arquivo: se ele está aberto para leitura/escrita ou se o

arquivo está aberto de forma síncrona. Essas flags estão definidas em <linux/fcntl.h>mode_t f_mode: Permissões de leitura e escrita do arquivo em questão, identificada pelos

bits FMODE_READ e FMODE_WRITE. Uma tentativa de leitura/escrita no arquivo que sem per-missões para tanto são bloqueadas diretamente pelo kernel, sem nem mesmo chegar a informaro seu módulo.

loff_t f_pos: Informa a posição da cabeça de leitura no arquivo. Essa posição nunca deveser alterada diretamente. As funções read() e write() implementadas em seu driver devem, naverdade, alterar o ponteiro de offset passado a elas (que, no caso, aponta para o f_pos dentrodessa struct). No caso de você ter passado batido pela file_operations acima, eu vou trazer afunção write() pra você dar uma olhada: ssize_t (*write) (struct file *, const char __user *, size_t,loff_t *). Observe que o último parâmetro é um ponteiro do tipo loff_t. A sua função deve alteraresse ponteiro, ao invés de buscar f_pos na estrutura de arquivo. A única função que altera essainformação diretamente é llseek, pois, afinal de contas, esse é o seu propósito.

struct file_operations * f_op: A struct que observamos anteriormente. Cada arquivo abertono kernel deve ter uma f_op associada com as funções disponíveis. Uma coisa interessante éque o endereço filp->f_op não fica guardado em nenhuma variável. Isso significa que sempreque o kernel precisa acessar alguma função daquele arquivo, ele vai buscar em filp->f_op, eisso possibilita algo muito importante: você pode mudar o endereço de f_op no filp para cadaminor number em um arquivo. Em outras palavras, isso possibilita um mesmo driver controlarde forma diferente variações do mesmo dispositivo, registrando-o com minor number diferente ecarregando uma f_op na memória moldada às necessidades.

void * private_data: Ponteiro para um bloco de dados para uso particular do arquivo. É útilcomo uma região de memória que pode ser utilizada por diversas chamadas de sistema do seumódulo. É de sua escolha utilizá-lo ou não, mas lembre-se de que qualquer região de memó-ria alocada num módulo pertence à memória controlada pelo kernel e deve ser cuidadosamentetratada. Não esqueça de desalocá-la quando terminar de usar ou quando o módulo for descarre-gado.

Esses são os pontos mais importantes sobre a struct file para nós. Os outros também sãoimportantes, claro, porém são mais específicos ao arquivo em si e não para nosso driver.

38

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

6.3 Estrutura de Inode

6.3.1 Links e inodes

A última struct que vamos dar uma olhada é a de inode. Como você deve(ria?) saber, inodeé uma entrada que descreve o arquivo num sistema de arquivos (file system). Você já criou linksno Linux, não é?

Aulinha de links, com o professor Leonardo:Existem dois tipos de links entre arquivos no linux. São chamados de soft e hard links. Soft

links apontam para um nome de um arquivo, e se você muda o nome do arquivo que ele aponta,o link é facilmente quebrado. Já o hard-link é um compromisso com a entrada no sistema dearquivos, e não com o nome do arquivo. Isso quer dizer que se você faz um hard-link com umarquivo, está efetivamente apontando para a posição no disco e não para o nome do arquivo.Qual a vantagem? Você me pergunta. Hard-links criam arquivos linkados independentes donome. Significa que alterando o conteúdo de um arquivo você altera o do outro, independente donome deles.

Um inode comum pertence à tabela de arquivos do sistema de arquivos, e cada inode contémmuitas infomações, além de dizer em que posição o arquivo está no disco. Um link apontandopara o inode vai estar lá enquanto o inode existir. Arquivos apontando para inodes que nãoexistem ou inodes sem ninguém apontando para eles são reflexos de inconsistências no sistemade arquivos (como acontece naquelas vezes em que acaba a energia ou quando você desliga ocomputador na pancada).

- Guarde assim: inodes são únicos, nomes de arquivo, não. Da mesma forma que em umsistema de arquivos eu posso ter vários nomes apontando para o mesmo inode, no kernel dolinux eu posso ter vários descritores de arquivo apontando para o mesmo inode virtual. Portanto,o inode é uma estrutura muito responsável, pois ela guarda a localização no sistema de arquivos,tamanho e muitas outras coisas sobre o arquivo.

6.3.2 inode

A estrutura inode é muito grande e sua totalidade está fora das intenções desse curso, masexistem dois pontos nela que devem ser observados:

dev_t i_rnode; para arquivos que são drivers de dispositivo, essa variável representa o seunúmero (major e minor nubers, lembra?). Vale lembrar que o melhor é não trabalhar com essa va-riável diretamente, pois sua história mostra que sua estrutura é bem diferente em muitas versõesde kernel. O melhor é usar duas funções para recuperar o número diretamente do inode:unsigned int iminor(stru t inode *inode);unsigned int imajor(stru t inode *inode);

Agora, vejamos essa union:union {stru t pipe\_inode\_info *i\_pipe;stru t blo k\_devi e *i\_bdev;stru t dev *i\_ dev;};39

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

Bom, cada membro dessa união representa, para a struct inode, o tipo de dispositivo. Se vocêutiliza o campo i_cdev, está "dizendo"que aquele inode representa um driver para um dispositivode carctere. i_bdev para dispositivos de bloco e i_pipe para pipes (pipes são regiões de memóriaque se comportam como uma fila. São criados com o comando mkfifo, análogo ao mknod. Nãoabordaremos pipes por enquanto). Vamos continuar usando dispositivos de caractere, pois sãomais simples de implementar. Logo veremos suas funções de registro.

A estrutura de inode e de file não são de nossa responsabilidade: elas são entregues prontaspara nós. Em outras palavras isso significa que não temos tenho que preencher a estrutura filee nem a inode. Isso seria um completo martírio, e escrever driver ia ser uma coisa para pessoascom MUITA paciência. Preencher a fops já é uma luta... imagina essas duas aí. Essas estruturasque são passadas para nós foram criadas pelo próprio kernel quando um programa executa achamada open(). Precisamos dela para saber que tipo de arquivo está sendo aberto e os modosdele. Bem como a posição da cabeça, entre outras coisas.

Vamos tentar tornar o nosso driver funcional agora. A primeira coisa que deve ser feita é pedirao kernel um pouco de espaço referente a um dispositivo de caractere:

struct cdev * dev_alloc();Essa função aloca espaço e inicializa algumas coisas da estrutura cdev, e retorna um ponteiro

para ela. Devemos inicializá-la completamente comvoid cdev_init(struct cdev *dev, struct file_operations *f ops);Agora o kernel sabe quais operações o driver sabe executar em cima do dispositivo, colocando

a fops em cdev. Depois de devidamente inicializado, vamos dizer ao kernel que estamos prontospara funcionar.

int cdev_add(struct cdev *dev, dev_t num, unsigned int coun t);Os argumentos são o ponteiro para o dispositivo, o número do dispositivo e a quantidade de

dispositivos consecutivos que são controlados por esse driver. Fique muito atento a essa função,pois, uma vez que o kernel foi informado dessa struct e de sua respectiva fops, ele pode começara utilizá-la imediatamente, estando o seu módulo pronto para isso ou não. Esse comportamentopode levar a falhas catastróficas (como um certo SO popular gosta muito de fazer) do sistema.Portanto, registrar essa cdev deve ser a última coisa que a sua função de inicialização deve fazer.

40

Capítulo 7

Mydev

Sabendo todas as estruturas importantes, já é hora de fazer algo mais funcional: Vamos agoracriar um dispositivo - mydev.c - que faz algo mais útil. Dê uma olhada:/*----------------------- ut here ---------------------------- */#in lude <linux/init.h>#in lude <linux/module.h>#in lude <linux/types.h>#in lude <linux/kdev\_t.h>#in lude <linux/fs.h>#in lude <linux/ dev.h>MODULE\_LICENSE("Dual BSD/GPL");stati int major;stati dev\_t dev;int open\_dev;stru t dev mydev\_ dev;/* Prototipos das fun oes prin ipais */int mydev\_open(stru t inode * inode, stru t file * filp);int mydev\_release(stru t inode * inode, stru t file * filp);ssize\_t mydev\_read(stru t file * filp, har \_\_user * buf, size\_t ount, loff\_t * f\_pos);ssize\_t mydev\_write(stru t file * filp, onst har \_\_user * buf, size\_t ount, loff\_t * f\_pos);stati int mydev\_init(void);stati void mydev\_exit(void);/* Ini ializa ao da fops */stru t file\_operations mydev\_fops = {.owner = THIS\_MODULE,.open = mydev\_open,.release = mydev\_release,.read = mydev\_read,.write = mydev\_write,};

41

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFstati int mydev\_init(void) {printk(KERN\_INFO "Carregando mydev\n");/* registra o driver om 3 minor numbers */if (allo \_ hrdev\_region(&dev, 0, 3, "driver")) {printk(KERN\_INFO "Houve um problema alo ando o driver\n");return 1;}/* alo a registra a estrutura dev no kernel */ dev\_init(&mydev\_ dev, &mydev\_fops);mydev\_ dev.owner = THIS\_MODULE; dev\_add(&mydev\_ dev, dev, 3);major = MAJOR(dev);printk(KERN\_INFO "Nosso major number e %d\n", major);open\_dev = 0;return 0;}stati void mydev\_exit(void) {printk(KERN\_INFO "Des arregando driver\n"); dev\_del(&mydev\_ dev);unregister\_ hrdev\_region(dev, 3);open\_dev--;}int mydev\_open(stru t inode * inode, stru t file * filp) {if (open\_dev) {return -EBUSY;}printk(KERN\_INFO "Dispositivo sendo usado\n");return 0;}int mydev\_release(stru t inode * inode, stru t file * filp) {printk(KERN\_INFO "Dispositivo liberado\n");return 0;}ssize\_t mydev\_write(stru t file * filp, onst har \_\_user * buf, size\_t ount, loff\_t * f\_pos) {printk(KERN\_INFO "Mensagem re ebida. Seu tamanho e %d\n", ount);return ount;}ssize\_t mydev\_read(stru t file * filp, har \_\_user * buf, size\_t ount, loff\_t * f\_pos) {printk(KERN\_INFO "Tentativa de leitura de uma mensagem de tamanho %d\n", ount);return ount;}42

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFmodule\_init(mydev\_init);module\_exit(mydev\_exit);/*----------------------- ut here ---------------------------- */Puxa vida! Tá aí, um driver legal de se ver. É excelente para estudar como são feitas as chamadasde sistema de cada aplicativo. Nosso driver não faz nada de verdade, só mostra o que está sendopassado a ele. Impressionante, não é? Dava para imaginar, no início do curso, que a gente ia veralgo bacana como isso? Observe as diferenças.

O que tem de novo? Bom, registramos a cdev, colocamos a fops e agora exibimos mensagenssobre o que está sendo passado a nós. Compile e carregue na memória. Não esqueça de fazer oarquivo de dispositivo correspondente (com mknod, lembra-se?). Teste-o com o comando echo,dd, cat, etc. Observe como cada um faz um pedido diferente em relação ao tamanho de dados.

Excelente. Agora, você pode se perguntar: por que diabos quando eu digito ’cat nome_do_dispositivo’,ele tenta ficar lendo indefinidamente? Não sei se você lembra, mas a função read() retorna umnúmero. Número positivo indica quantos bytes ele leu, e número negativo, um erro qualquer. O 0(zero) significa fim do arquivo. cat lê até o fim do arquivo, mas nós nunca retornamos 0 em read().O que acontece com a região de memória que o read() passa para preenchermos? Nada. Ficalá, do jeito que veio, volta.

43

Capítulo 8

A memória e o Kernel

Vamos entender como funciona a alocação de memória dentro do kernel - zonas de memória,alocação dinâmica, etc

8.1 Como funciona a memória?

Deixe-me explicar um pouco sobre a memória no Linux.Basicamente, na hora do boot, o kernel divide a memória do seu computador em três zonas:

a primeira é vista de dentro do kernel, e pode ser acessada diretamente por ele, essa zona échamada de zona normal ou zona baixa de memória. Atualmente seu tamanho máximo chegaa 1 GB (o que é muito mais do que necessário para um kernel de Linux). A segunda zonaé chamada de zona alta de memória, e pode ir muito mais além de 1GB. É acessada fora dokernel, no userspace. A terceira zona de memória é chamada de DMA - Direct Memmory Access(Acesso direto a memória) e geralmente é parte de algum dispositivo e muito dependente daarquitetura. Cada uma das zonas de memória é mapeada numa região diferente, e, assim sendo,"ponteiros"que vêm do userspace podem ser inválidos dentro do kernel space e vice-versa. Porisso que o buffer enviado a read() e write() no exemplo anterior ficou intocado: antes de mexer comeles, é necessário entender esses conceitos. Sem contar que, se tentamos acessar o ponteiropassado em buf, a região de memória referenciada por ele pode estar inacessível no momento naRAM, gerando um page fault. Em um programa normal, tudo bem, é só esperar um pouco que odado vai ser acessível. No kernel isso é simplesmente inadmissível! Não dá para ficar esperandoa boa vontade da memória. E como vamos fazer pra utilizar os dados? Bom, tem duas funçõesque servem só para isso, definidas em asm/uaccess.h: copy_from_user() e copy_to_user().

unsigned long copy_to_user(void __user *to, const void *fr om, unsigned long count);unsigned long copy_from_user(void *to, const void __user * from, unsigned long count);

Na primeira, *to é um ponteiro do userspace que receberá os dados, *from é um ponteiro dokernel space de onde os dados serão copiados e count é o tamanho em bytes a ser copiado de*from para *to. Na segunda, copy_from_user(), *to é um ponteiro do kernel space, *from é umponteiro do user space e count é a quantidade de dados que vai ser copiada de *from para *to.Tão simples que eu nem precisava ter explicado, não é?

Essas duas funções, meus caros alunos, são duas das engrenagens principais de um dri-ver de dispositivo. Sem essas funções, a comunicação userspace<->kernelspace teria de serreinventada cada vez que um novo driver fosse criado.

Agora, pense nas possibilidades! Ou melhor, pense no /dev/zero. Na verdade, pense nosdispositivos virtuais controlados pelo driver mem. Você pode até olhar em drivers/char/mem.c pra

44

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

ter uma idéia. Bom, o que o /dev/zero faz? Quando você lê dele, enche a saída de zeros. Masé zero no sentido de NADA. Não é o zero decimal, nem o de caractere. É NADA mesmo. NULL.Quando você escreve nele, faz a mesma coisa que escrever em /dev/null.

Mas, fazer um driver desses é fácil, a gente já dá conta com copy_to_user(). Mas ainda estáfaltando uma coisa... a função copy_to_user precisa de um ponteiro para o buffer que queremoscopiar, e esse buffer deve variar seu tamanho de acordo com count (que nos é passado atravésda chamada da função mydev_read(..., size_t count)). Não dá para simplesmente enviar um "0"no lugar do ponteiro para o buffer e retornar o count que nos foi passado; os resultados seriamimprevisíveis.

Então vou apresentar as funções de tratamento de memória do kernel, para resolver esseproblema. Elas estão definidas em linux/slab.h e são tão parecidas com as funções de userspaceque, se eu não falasse, você nem ia notar a diferença.

A primeira delas é a de alocação de memória:void *kmallo (size\_t size, gfp\_t flags);E a segunda é a de liberação:void kfree(void *ptr);kmalloc() funciona como malloc(); retorna um ponteiro para a região alocada de memória,

size_t size é o tamanho que você quer alocar e gfp_t flags define como vai se comportar essafunção. Calma, vou explicar. Algumas situações, você não precisa de acesso instantâneo àmemória do kernel e pode esperar até que alguma região de memória seja liberada para você;para isso, você usa a flag GFP_KERNEL. Em outras situações, você precisa instantaneamente dememória, pois o dispositivo e o driver não podem parar. Essa é a flag GFP_ATOMIC. Existe aindasituações em que você quer que a memória alocada esteja disponível no userspace. Para tanto,utilize a flag GFP_USER, que aloca e mapeia a memória para o userspace ou GFP_USERHIGH,que mapeia para o userspace, mas aloca somente na zona alta de memória. Existem ainda flagsmais específicas, como é o caso de __GFP_DMA, que diz à função kmalloc() que a memória a seralocada é capaz de operações em DMA. Há outras flags, mas essas são as mais interessantes.kmalloc, bem como malloc, retorna NULL caso seja impossível alocar a memória requisitada.

kfree() age exatamente igual a free(). Libera uma região de memória. Trabalhando com okernel, extremo cuidado com a memória. Lembre-se que a memória no kernelspace não é grandee deve ser poupada.

Existem algumas outras funções associadas à kmalloc, que são muito úteis também. Elas sãovoid *k allo (size\_t n, size\_t size, gfp\_t flags);void *kzallo (size\_t size, gfp\_t flags);void *memset(void *,int,\_\_kernel\_size\_t);kcalloc cria um array de n elementos, cada um com o tamanho size, e devolve um ponteiro paraessa array; kzalloc devolve um ponteiro para a memória alocada, "zerada"por omissão; memseté equivalente à memset da libc, ou seja, seta toda a região de memória de acordo com seusparâmetros.

45

Capítulo 9

Mydev completo

Vamos agora finalizar o nosso driver

9.1 mydev.c

Vamos para a última parte do nosso curso. O driver, com algumas das funções que aprende-mos durante todas as aulas, será apresentado de forma completa e funcional. Ele agora é capazde armazenar um valor na memória do kernel, que também poderá ser acessado. No final dessaparte vamos executar alguns programas em nosso dispositivo virtual, só para ver o que acontece./* ---------------------------------- ut here ------------------------ */#in lude <linux/init.h>#in lude <linux/module.h>#in lude <linux/types.h>#in lude <linux/kdev\_t.h>#in lude <linux/fs.h>#in lude <linux/ dev.h>#in lude <linux/slab.h>#in lude <linux/string.h>#in lude <asm/ua ess.h>MODULE\_LICENSE("Dual BSD/GPL");stati int major;stati dev\_t dev;stati int open\_dev;stati stru t dev mydev\_ dev;stati har * internal\_buffer;/* Prototipos das fun oes prin ipais */stati int mydev\_open(stru t inode * inode, stru t file * filp);stati int mydev\_release(stru t inode * inode, stru t file * filp);stati ssize\_t mydev\_read(stru t file * filp, har \_\_user * buf, size\_t ount, loff\_t * f\_pos);stati ssize\_t mydev\_write(stru t file * filp, onst har \_\_user * buf, size\_t ount, loff\_t * f\_pos);stati int mydev\_init(void);

46

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFstati void mydev\_exit(void);/* Ini ializa ao da fops */stru t file\_operations mydev\_fops = {.owner = THIS\_MODULE,.open = mydev\_open,.release = mydev\_release,.read = mydev\_read,.write = mydev\_write,};stati int mydev\_init(void) {printk(KERN\_ALERT "Carregando mydev\n");/* registra o driver om 3 minor numbers */if (allo \_ hrdev\_region(&dev, 0, 3, "mydev")) {printk(KERN\_ALERT "Houve um problema alo ando o driver\n");return 1;}/* alo a registra a estrutura dev no kernel */ dev\_init(&mydev\_ dev, &mydev\_fops);mydev\_ dev.owner = THIS\_MODULE; dev\_add(&mydev\_ dev, dev, 3);major = MAJOR(dev);printk(KERN\_ALERT "Nosso major number e %d\n", major);open\_dev = 0;internal\_buffer = NULL;return 0;}stati void mydev\_exit(void) {printk(KERN\_ALERT "Des arregando driver\n"); dev\_del(&mydev\_ dev);unregister\_ hrdev\_region(dev, 3);open\_dev--;}int mydev\_open(stru t inode * inode, stru t file * filp) {if (open\_dev) {return -EBUSY;}printk(KERN\_ALERT "Dispositivo sendo usado\n");return 0;}47

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DFint mydev\_release(stru t inode * inode, stru t file * filp) {printk(KERN\_ALERT "Dispositivo liberado\n");return 0;}ssize\_t mydev\_write(stru t file * filp, onst har \_\_user * buf, size\_t ount, loff\_t * f\_pos) {printk(KERN\_ALERT "Mensagem re ebida. Seu tamanho e %d\n", ount);if (internal\_buffer != NULL)kfree(internal\_buffer);internal\_buffer = kmallo ( ount, GFP\_KERNEL);memset(internal\_buffer,'\0', ount); // Memoria limpa, por favor!if ( opy\_from\_user(internal\_buffer, buf, ount))printk(KERN\_ALERT "Erro ao opiar dados do userspa e\n");return ount;}ssize\_t mydev\_read(stru t file * filp, har \_\_user * buf, size\_t ount, loff\_t * f\_pos) {int size;printk(KERN\_ALERT "Tentativa de leitura de uma mensagem de tamanho %d\n", ount);if (internal\_buffer != NULL)size = strlen(internal\_buffer) +1; // Nao podemos esque er do \0elsereturn 0;if (*f\_pos >= size)return 0;if ( ount > size) // Nao vamos ler mais do que tem pra ler ount = size;if ( opy\_to\_user(buf, internal\_buffer, ount)) {printk(KERN\_ALERT "Erro ao opiar dados para userspa e\n"); // Passando do buffer pro usuarioreturn 0;}*f\_pos += ount;return ount;}module\_init(mydev\_init);module\_exit(mydev\_exit);/* ---------------------------------- ut here ------------------------ */9.2 Utilização do mydev

Enfim, aí está. Primeiro, declaramos globalmente um ponteiro que vamos utilizar como bufferinterno. Tudo que for escrito no dispositivo, vai estar dentro desse ponteiro. Tudo que for lido, vai

48

CDTC Centro de Difusão de Tecnologia e Conhecimento Brasília/DF

ser lido desse ponteiro. Em um dispositivo de verdade estaríamos apontando para uma regiãode memória própria dele. No entanto, nosso driver ainda seria responsável pela manipulação dooffset, da escrita e da leitura, além de outras coisas. A inicialização e finalização do driver vocêsjá conhecem. O que mudou foram as funções de leitura e escrita. Acrescentamos um montede checagens de memória. Vamos para a função de escrita. A primeira coisa é ver se o bufferexiste. Se não, a gente cria um. Depois escrevemos os dados que o programa está mandandono buffer. Se houver algum erro (copy_to_user e copy_from_user retornam o número de bytesque não puderam ser enviados), a gente exibe a mensagem de erro. Nada demais. A função deleitura do dispositivo é um pouquinho mais trabalhosa. Temos que checar, primeiro, se existe oque ser lido. Checamos se o tamanho de dados que está sendo pedido cabe dentro do tamanhodos dados que temos, para que não haja nenhuma violação de memória. Por último, verificamosse estamos no final do buffer, retornando 0 caso sim (simbolizando o final do arquivo). Mandamosos dados para o usuário e ajustamos a cabeça de leitura (o offset, f_pos). É complicado? Não,né? Moleza Antes de testar, tenha certeza que criou o arquivo de dispositivo correspondentecom mknod e que descarregou (caso esteja carregado) e carregou novamente o driver. Caso asmensagens não estejam sendo exibidas no terminal, fique de olho no arquivo /var/log/syslog. Vouchamar o arquivo de dispositivo de mydev, só pra ajudar nos exemplos.

Primeiro teste básico: echo "oi driver de dispositivo» mydevVerifique o arquivo de log, e vai perceber que chegou a mensagem.cat mydevDeve exibir o que foi gravado.Você pode fazer tambémcat <algum arquivo> > mydevE todo o conteúdo do arquivo será copiado.Você pode testar o que mais quiser.Apesar do driver ser bem simples, ele possui alguma utilidade. Pode ser uma região de

memória que pode ser usada pelo seu programa. Pode ser tanta coisa interessante, e dá parafazer muito mais!

49