Você está na página 1de 451

Linux Device Drivers

Embedded Labworks

Por Sergio Prado. So Paulo, Novembro de 2012 Copyright Embedded Labworks 2004-2013. All rights reserved.

Embedded Labworks

SOBRE ESTE DOCUMENTO

Este documento baseado no material de treinamento disponibilizado pela Free Electrons em: http://free-electrons.com/doc/training/linux-kernel Este documento disponibilizado sob a Licena Creative Commons BY-SA 3.0.
http://creativecommons.org/licenses/by-sa/3.0/legalcode

Os fontes deste documento esto disponveis em: http://e-labworks.com/treinamentos/drivers/source

Embedded Labworks

SOBRE O INSTRUTOR

Sergio Prado tem mais de 15 anos de experincia em desenvolvimento de software para sistemas embarcados, em diversas arquiteturas de CPU (ARM, PPC, MIPS, x86, 68K), atuando em projetos com Linux embarcado e sistemas operacionais de tempo real. scio da Embedded Labworks, onde atua com consultoria, treinamento e desenvolvimento de software para sistemas embarcados: http://e-labworks.com Mantm um blog pessoal sobre Linux e sistemas embarcados em: http://sergioprado.org

Embedded Labworks

AGENDA DO TREINAMENTO

DIA 1: Introduo ao kernel Linux, mdulos do kernel, dispositivos de hardware, introduo ao device model, hardware I/O. DIA 2: Gerenciamento de processos, trabalhando com interrupes, mecanismos de sincronizao, kernel debugging. DIA 3: Unified Device Model, camada TTY, infraestrutura de barramento, platform driver, frameworks.

Embedded Labworks

DURANTE O TREINAMENTO

Pergunte... Expresse seu ponto de vista... Troque experincias... Ajude... Participe!

Embedded Labworks

AMBIENTE DE LABORATRIO
/opt/labs/ dl/ docs/ guides/ hardware/ training/ ex/ tools/ Ambientedelaboratrio Aplicaesepacotesopensource Queserousadosduranteas atividadesdelaboratrio Documentao Guiaselivrosdeconsulta Documentaodohardware Slideseatividadesdelaboratrio. Exercciosdelaboratrio Ferramentasdeusogeral

Embedded Labworks

Linux Device Drivers

Introduo ao kernel Linux

Embedded Labworks

LINUX, SIMPLES ASSIM... :)

Embedded Labworks

HISTRICO

O kernel Linux um dos componentes do sistema operacional, que requer bibliotecas e aplicaes para prover funcionalidades aos usurios. Foi criado em 1991 por um estudante finlands, Linus Torvalds, e comeou a ser usado rapidamente como sistema operacional em projetos de software livre. Linus foi capaz de criar uma comunidade grande e dinmica de desenvolvedores e usurios ao redor do projeto. Atualmente, centenas de pessoas e empresas contribuem com o Linux.

Embedded Labworks
Linux 3.2

Embedded Labworks

PRINCIPAIS CARACTERSTICAS

Extremamente portvel: suporte para mais de 25 arquiteturas e milhares de dispositivos de hardware. Modular: capaz de rodar apenas o que necessrio para o projeto. Escalvel: o mesmo kernel roda em relgios, celulares e servidores da bolsa de valores! Seguro: sistema aberto, revisado por muitos experts, no tem como esconder falhas.

Embedded Labworks

PRINCIPAIS CARACTERSTICAS (cont.)

Estvel: capaz de rodar por muito tempo sem precisar de um nico reboot. Compatvel com padres de mercado. Livre de royalties. Fcil de programar e com muitos recursos disponveis na Internet.

Embedded Labworks

ARQUITETURA GERAL
Aplicao Biblioteca Aplicao Biblioteca Biblioteca C Aplicao

User space

Chamadas de sistema

Notificao de eventos Exportao de informaes Kernel Linux

Gerenciamento do hardware Hardware

Notificao de eventos

Embedded Labworks

KERNEL SPACE x USER SPACE

Existe uma separao bem definida entre o kernel (kernel space) e as bibliotecas e aplicaes do usurio (user space). O kernel roda em modo privilegiado, com total acesso todas as instrues da CPU, endereamento de memria e I/O, enquanto que os processos do usurio rodam em modo restrito, com acesso limitado aos recursos da mquina. Por isso, existe uma interface de comunicao, baseada chamadas de sistema (system calls), para que as bibliotecas e aplicaes tenham acesso aos recursos da mquina.

Embedded Labworks

CHAMADAS DE SISTEMA

O Linux possui aproximadamente 300 chamadas de sistema. Operaes em arquivos, operaes de rede, comunicao entre processos, gerenciamento de processos, mapeamento de memria, timers, threads, mecanismos de sincronizao, etc. As chamadas de sistema so abstradas pela biblioteca C padro. As aplicaes normalmente no precisam fazer uma chamada direta. Tudo feito atravs da biblioteca C padro. A interface de chamadas de sistema bem estvel. Durante novos releases do kernel, apenas novas chamadas de sistema podem ser adicionadas.

Embedded Labworks

ESTRUTURA INTERNA

Embedded Labworks

SISTEMA DE ARQUIVO VIRTUAL

O Linux fortemente baseado em arquivos (quase tudo no sistema representado por um arquivo). O kernel implementa a camada VFS (Virtual Filesystem) que abstrai o acesso aos arquivos, possibilitando que rotinas de acesso ao arquivo (open, read, write, close, etc) sejam mapeadas para diferentes destinos.

Embedded Labworks

SISTEMA DE ARQUIVO VIRTUAL (cont.)

Exemplo 1: Mapeando um arquivo fsico em um dispositivo de armzenamento (copiando um arquivo do HD para um pendrive):
$cp/usr/sbin/app/mnt/pendrive/

Exemplo 2: Mapeando um arquivo virtual (listando as estatsticas de uso de memria do sistema):


$cat/proc/meminfo

Exemplo 3: Mapeando o acesso ao hardware (escrevendo na porta serial):


$echo"Teste">/dev/ttyS0

Embedded Labworks

FONTES DO KERNEL

A verso oficial do cdigo-fonte do kernel liberada por Linus Torvalds encontra-se em: http://www.kernel.org Baixando os fontes por http:
$wgethttp://www.kernel.org/pub/linux/kernel/v3.0/linux3.6.tar.bz2 $tarxjfvlinux3.6.tar.bz2

Baixando os fontes pelo git:


$gitclonegit://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

Embedded Labworks

FONTES DO KERNEL (cont.)

Muitas comunidades e fabricantes de hardware podem manter verses alternativas do kernel:

Fabricantes de hardware podem manter verses especficas do kernel com suporte s suas plataformas de referncia. Comunidades podem manter verses do kernel voltadas arquiteturas especficas (ARM, MIPS, PPC), sub-sistemas (USB, PCI, network), sistemas de tempo-real, etc.

Normalmente nestas verses alternativas so disponibilizados os patches para serem aplicados em uma determinada verso do kernel.

Embedded Labworks

VERSIONAMENTO

Antes da verso 2.6:

Uma rvore de verses estveis (1.0, 2.0, 2.2, 2.4) Uma rvore de verses de desenvolvimento (2.1, 2.3, 2.5)

A partir de 2003, apenas uma rvore: 2.6.X Em 2011, a verso mudou para 3.0.

Embedded Labworks

CICLO DE RELEASE

Processo de desenvolvimento aproximadamente a cada 3 meses:

Merge window: 2 semanas (at sair 3.X-rc1). Bug fixing: 6 a 10 semanas (3.X-rc2, 3.X-rc3, etc).

Em aproximadamente 3 meses temos a liberao do release final 3.X. Para acompanhar as mudanas no kernel: http://wiki.kernelnewbies.org/LinuxChanges http://lwn.net

Embedded Labworks

LINGUAGEM DE PROGRAMAO

Implementado em linguagem C como todos os sistemas UNIX. Um pouco de assembly usado no cdigo dependente de arquitetura em /arch (cdigo de inicializao, tratamento de excees, bibliotecas crticas, etc). Nada de C++! http://www.tux.org/lkml/#s15-3 Compilado normalmente com o GCC, j que usa algumas extenses especficas deste compilador.

Suporta tambm alguns compiladores especficos com o da Intel e o da Marvell. Existe uma iniciativa para usar o Clang do projeto LLVM.

Embedded Labworks

BIBLIOTECA C PADRO

O kernel uma aplicao standalone (baremetal) e no pode usar ou ser dependente de cdigo que roda em espao de usurio. Portanto, voc no pode usar funes da biblioteca C padro (memset(), malloc(), printf(), etc). Por este motivo, o kernel tem sua prpria implementao de rotinas comuns da biblioteca C (memset(), kmalloc(), kprintf(), etc).

Embedded Labworks

PORTABILIDADE

O kernel Linux projetado para ser extremamente portvel. Todo o cdigo fora de arch/ deve ser portvel. Para isso, o kernel prov diversas funes e macros para abstrair os detalhes especficos da arquitetura, por exemplo:

Endianness (cpu_to_be32, cpu_to_le32, be32_to_cpu, le32_to_cpu). Acesso I/O mapeado em memria. Memory barriers. DMA API.

Embedded Labworks

API INTERNA DO KERNEL

A API interna do Linux pode mudar entre duas verses estveis do kernel. Isso significa que um driver que foi desenvolvido para uma verso do kernel pode no funcionar na prxima verso. Mais informaes em Documentation/stable_api_nonsense.txt . Sempre que um desenvolvedor alterar uma API interna do kernel, ele responsvel por atualizar todo o cdigo que acessa esta API, garantindo que nada no kernel vai parar de funcionar. Funciona muito bem para todo cdigo na rvore oficial do kernel (mainline), mas pode quebrar drivers de cdigo fechado ou fora da rvore do kernel.

Embedded Labworks

LICENA

Todo o cdigo-fonte do Linux software livre e liberado sob a licena GPLv2. Isso significa que:

Quando voc receber ou comprar um equipamento com Linux, voc tem o direito de requisitar os fontes, alter-los e redistribu-los. Quando voc desenvolver um produto com Linux, voc precisa liberar os fontes do kernel sob as mesmas condies, sem restries.

Embedded Labworks

LICENA (cont.)

Portanto, ilegal distribuir um binrio do kernel com mdulos compilados estaticamente. Os mdulos do kernels so uma rea cinza: um trabalho derivado do kernel ou no?

A opinio geral da comunidade de que drivers fechados so ruins. Sob um ponto de vista legal, cada driver provavelmente um caso diferente. Ex: Nvidia. realmente til manter um driver proprietrio?

Embedded Labworks

VANTAGENS DE DRIVERS GPL

Voc no precisa escrever um driver do zero, podendo reusar o cdigo de outros drivers. Voc pode integrar o seu driver na rvore oficial do kernel, e no se preocupar com qualquer alterao em APIs internas do Linux. Custo zero de manuteno e melhorias no driver! Com drivers abertos voc tem suporte da comunidade, com mais pessoas revisando e colaborando com seu cdigo. Os usurios e a comunidade tem uma viso positiva da empresa.

Embedded Labworks

LABORATRIO

Estudando e navegando nos fontes do Linux

Embedded Labworks

Linux Device Drivers

Kit de desenvolvimento i.MX53 QSB

Embedded Labworks

I.MX53 QUICK START BOARD

Embedded Labworks

CARACTERSTICAS

CPU i.MX535 de 1GHz da Freescale (Cortex-A8). 1GB de memria RAM DDR3. Conector para carto SD/MMC, microSD e interface SATA. Sadas de udio estreo e vdeo VGA, e entrada para microfone. Conector de expanso com sadas HDMI, display LCD, cmera e SDIO. Interfaces USB host/device, Ethernet, UART, JTAG, botes, leds, etc.

Embedded Labworks

DIAGRAMA DE BLOCOS

Embedded Labworks

DOCUMENTAO

Documentao do hardware

i.MX53 Datasheet: CPU_DS_iMX53.pdf Board reference: BOARD_DS_IMX53.pdf User's Guide: BOARD_UG_IMX53.pdf Linux BSP: BSP_LINUX_mx53.pdf / BSP_LINUX_DOCS.tar.gz

Recursos na internet http://www.freescale.com/imxquickstart http://imxcommunity.org/

Embedded Labworks

CONECTANDO O HARDWARE

Embedded Labworks

LABORATRIO

Conectando e testando o hardware

Embedded Labworks

Linux Device Drivers

Configurando e compilando o kernel Linux

Embedded Labworks

CONFIGURANDO O KERNEL

O kernel possui centenas de drivers de dispositivo, diversos protocolos de rede e muitos outros itens de configurao. O kernel bem modular, so muitas as opes disponveis para serem habilitadas/desabilitadas. O processo de configurao serve para voc configurar o kernel para ser compilado para sua CPU/plataforma. O conjunto de opes que voc ir habilitar depende:

Do seu hardware (device drivers, etc). Das funcionalidades (protocolos de rede, sistemas de arquivo, etc).

Embedded Labworks

CONFIGURAO

As configuraes so salvas em um arquivo chamado .config no diretrio principal dos fontes do kernel, e possuem o formato key=value. Exemplo:
CONFIG_ARM=y

Dificilmente voc vai precisar editar o arquivo .config manualmente. Existem ferramentas de interface grfica para configurar o kernel e gerar o arquivo de configurao automaticamente:

make menuconfig make gconfig make xconfig make nconfig

Embedded Labworks

$ make xconfig

Embedded Labworks

$ make gconfig

Embedded Labworks

$ make nconfig

Embedded Labworks

$ make menuconfig

Embedded Labworks

CONFIGURANDO O KERNEL (cont.)

O kernel um binrio nico, resultado do processo de linkagem de todos os arquivos-objeto das funcionalidades habilitadas, incluindo os device drivers. O kernel permite que algumas das funcionalidades disponveis possam ser habilitadas e compiladas de duas formas:

Esttica ou built-in: a funcionalidade selecionada linkada estaticamente imagem final do kernel. Dinmica ou mdulo: gerado um mdulo daquela funcionalidade (arquivo com extenso .ko). Este mdulo no includo na imagem final do kernel. Ele includo no sistema de arquivos e pode ser carregado dinamicamente (em tempo de execuo), conforme a necessidade.

Embedded Labworks

OPES DE CONFIGURAO

Opes booleanas (verdadeiro/falso)


[]Opodesabilitada [*]Opohabilitada

Opes de 3 estados:
<>Opodesabilitada <*>Opohabilitada(builtin) <M>Opohabilitada(mdulo)

Nmeros inteiros. Ex: (17)Kernellogbuffersize Strings. Ex: (iso88591)DefaultiocharsetforFAT

Embedded Labworks

DEPENDNCIAS

Na configurao do kernel, podem existir dependncias entre funcionalidades:

Exemplo 1: o driver de um dispositivo I2C depende da habilitao do barramento I2C para ser habilitado. Exemplo 2: o driver do barramento USB habilitado automaticamente quando o driver de um dispositivo USB habilitado.

Embedded Labworks

CONFIGURAO POR ARQUITETURA

Toda a configurao do kernel dependente da arquitetura. Por padro, o kernel considera um build nativo, ento ir usar a arquitetura da mquina de desenvolvimento (normalmente x86) no comando abaixo: $makemenuconfig Portanto, para configurar para ARM por exemplo, voc precisa especificar a arquitetura: $makeARCH=armmenuconfig Ao invs de passar a varivel ARCH na chamada do make, voc pode tambm defini-la como varivel de ambiente ou alterar o arquivo Makefile no diretrio principal do kernel.

Embedded Labworks

CONFIGURAES PR-DEFINIDAS

Arquivos de configurao pr-definidos para diversas plataformas esto disponveis em arch/<arch>/configs/. O uso de arquivos pr-configurados a forma padro de configurar um kernel para uma plataforma especfica. Por exemplo, para carregar a configurao padro do kit de desenvolvimento i.MX53 Quick Start Board:
$makeARCH=armimx_v6_v7_defconfig

Se voc alterou a configurao padro e deseja salv-la, pode criar uma cpia conforme exemplo abaixo:
$cp.configarch/<arch>/configs/myconfig_defconfig

Embedded Labworks

COMPILANDO O KERNEL

Depois de configurado, para compilar nativamente basta executar: $make No precisa de previlgios de root! Para cross-compilar, voc precisa indicar a arquitetura e o prefixo do cross-compiler. Exemplo: $makeARCH=armCROSS_COMPILE=armlinux O comando acima ir gerar uma imagem genrica para ARM. Se voc quiser gerar uma imagem especfica para determinado bootloader, deve adicionar ao fim do comando o nome da imagem. Exemplo para o U-Boot: $makeARCH=armCROSS_COMPILE=armlinuxuImage

Embedded Labworks

COMPILANDO OS MDULOS

Para compilar apenas os mdulos, basta executar:


$makemodules

Para cross-compilar os mdulos, no esquea de indicar a arquitetura e o prefixo do cross-compiler. Exemplo:


$makeARCH=armCROSS_COMPILE=armlinuxmodules

Embedded Labworks

COMPILANDO O KERNEL (cont.)

Ao fim do processo de compilao, sero geradas as seguintes imagens:

vmlinux: imagem do kernel no formato ELF, que no inicializvel,

mas pode ser usada para debugging.

*.ko: mdulos do kernel, dentro de seus respectivos diretrios.

Em arch/<arch>/boot/:

Image: imagem final do kernel, inicializvel e descomprimida. *Image: imagem inicializvel e comprimida do kernel (bzImage para x86, zImage para ARM, etc). uImage: imagem do kernel para o U-Boot (opcional).

Embedded Labworks

INSTALANDO O KERNEL

Para instalar o kernel, basta executar o comando abaixo: $makeinstall Este comando ir instalar os seguintes arquivos no diretrio /boot:

vmlinuz<version> (imagem do kernel comprimida) System.map<version> (endereos dos smbolos do kernel) config<version> (arquivo de configurao do kernel)

Normalmente no usado em sistemas embarcados! Em sistemas embarcados, normalmente gravamos o kernel em um dispositivo de armazenamento (carto SD, memria flash, etc).

Embedded Labworks

INSTALANDO OS MDULOS

Para instalar os mdulos, basta executar o comando abaixo:


$makemodules_install

No caso de um ambiente de compilao cruzada, os mdulos devem ser instalados no rootfs do target. Para isso, devemos passar o parmetro INSTALL_MOD_PATH no comando de instalao:
$makeARCH=<arch>INSTALL_MOD_PATH=<dir>modules_install

Embedded Labworks

FAZENDO A LIMPEZA

Remove todos os arquivos gerados (imagens, arquivos-objeto, etc). $makeclean Remove todos os arquivos de gerados e arquivos de configurao (usado quando pretende-se mudar de plataforma). $makemrproper Alm dos arquivos gerados e arquivos de configurao, remove tambm arquivos de backup (bom para gerar patches). $makedistclean

Embedded Labworks

LINHA DE COMANDOS DO KERNEL

Ao ser carregado, o kernel pode receber um conjunto de parmetros. Chamamos esses parmetros de linha de comandos do kernel. Esta linha de comandos pode ser passada ao kernel de duas formas diferentes:

Pelo bootloader. Hardcoded configurao CONFIG_CMDLINE. na do kernel, atravs da opo

Esta linha de comandos uma string com diversas opes no formato key=value.

Embedded Labworks

LINHA DE COMANDOS DO KERNEL (cont.)


console=ttySAC0root=/dev/mtdblock3rootfstype=jffs2

Exemplo de linha de comandos do kernel, onde:

console = dispositivo que ser usado como console root = dispositivo onde se encontra o sistema de arquivos rootfstype = tipo do sistema de arquivos (JFFS2)

Existem dezenas de outras opes! Documentao disponvel em:


Documentation/kernelparameters.txt

Embedded Labworks

LABORATRIO

Compilando o kernel e testando o sistema

Embedded Labworks

Linux Device Drivers

Mdulos do kernel

Embedded Labworks

MDULOS

Internamente, o Linux bem modular. Cada funcionalidade abstrada em um mdulo, com uma interface de comunicao bem definida. Por isso, permite um sistema de configurao onde voc pode adicionar/remover determinada funcionalidade. E o Linux permite que voc adicione dinamicamente pedaos de cdigo do kernel em tempo de execuo! Chamamos esses pedaos de cdigo de mdulos do kernel. Um device driver pode ser compilado de forma integrada ao kernel (built-in) ou como um mdulo do kernel.

Embedded Labworks

VANTAGENS DOS MDULOS

Ajuda a manter a imagem do kernel bem pequena. Mdulos tornam fcil o desenvolvimento do kernel, como por exemplo device drivers, sem precisar reiniciar o equipamento. O tempo de boot fica menor: menos cdigo para ser executado na inicializao do kernel. Cuidado: mdulos rodam em kernel space. Uma vez carregados, eles tem total controle do sistema! Por isso s podem ser carregados como root.

Embedded Labworks

DEPENDNCIAS DOS MDULOS

Alguns mdulos dependem de outros mdulos, que precisam ser carregados primeiro. Exemplo: o mdulo usb_storage depende do mdulo usbcore. As dependncias entre os mdulos esto descritas no arquivo /lib/modules/<kernelversion>/modules.dep . Este arquivo gerado automaticamente quando voc instala os mdulos.

Embedded Labworks

CARREGANDO UM MDULO

$insmod<module_path>.ko Carrega apenas um mdulo. necessrio passar o caminho completo do mdulo. $modprobe<module_name> Carrega um mdulo e todas as suas dependncias. Deve-se passar apenas o nome do mdulo, sem a extenso .ko e sem seu caminho completo.

Embedded Labworks

DESCARREGANDO UM MDULO

$rmmod<module_name> Descarrega apenas um mdulo. Possvel apenas se o mdulo no estiver mais em uso. Deve-se passar apenas o nome do mdulo, sem a extenso .ko e sem seu caminho completo. $modprober<module_name> Descarrega um mdulo e todas as suas dependncias (que no esto sendo usadas). Deve-se passar apenas o nome do mdulo, sem a extenso .ko e sem seu caminho completo.

Embedded Labworks

LISTANDO INFORMAES DOS MDULOS

$modinfo<module_name> L informaes de um mdulo, como sua descrio, parmetros, licena e dependncias. Deve-se passar apenas o nome do mdulo, sem a extenso .ko e sem seu caminho completo. $lsmod Lista todos os mdulos carregados.

Embedded Labworks

PASSANDO PARMETROS

Descobrindo os parmetros disponveis do mdulo: $modinfo<module> Passando um parmetro via insmod: $insmod<module>param=value Para passar um parmetro via modprobe, configurar o parmetro em /etc/modprobe.conf ou em um arquivo em /etc/modprobe.d/: options<module>param=value Para passar um parmetro via linha de comandos do kernel: <module>.param=value

Embedded Labworks

LOGS DO KERNEL

Quando um novo mdulo carregado, informaes relevantes so exibidas no log do kernel. O kernel mantm um log de mensagens na memria em um buffer circular. Este log de mensagens esta disponvel atravs do comando dmesg (diagnostic message). Mensagens de log tambm so exibidas na console. Voc pode filtrar estas mensagens atravs do parmetro loglevel ou desabilitar completamente atravs do parmetro do kernel quiet.

Embedded Labworks

Linux Device Drivers

Desenvolvendo um mdulo do kernel

Embedded Labworks

#include<linux/module.h> #include<linux/kernel.h> /*moduleinitialization*/ staticint__initmymodule_init(void) { printk("Mymoduleinitialized.\n"); return0; } /*moduleexit*/ staticvoid__exitmymodule_exit(void) { printk("Exitingmymodule.\n"); } module_init(mymodule_init); module_exit(mymodule_exit); MODULE_LICENSE("GPL");

__init removido aps a inicializao (built-in ou mdulo)

__exit descartado caso seja compilado estaticamente no kernel (built-in).

Embedded Labworks

O PRIMEIRO MDULO

A macro module_init() declara a funo de inicializao que ser chamada ao carregar o mdulo para a memria. A funo de inicializao nomeada de acordo com o padro <modulename>_init(), responsvel por inicializar o mdulo e retornar 0 para OK ou um valor negativo em caso de erro. removida da memria assim que executada. A macro module_exit() declara a funo de limpeza, que ser chamada assim que o mdulo for descarregado da memria. Se o mdulo for compilado estaticamente, esta funo no utilizada, e por este motivo, nem carregada para a memria.

Embedded Labworks

METADADOS DO MDULO

Voc pode declarar informaes (metadados) dos mdulos usando as seguintes macros:

MODULE_LICENSE(): Declara a licena do mdulo. MODULE_DESCRIPTION():

Mensagem

descritiva

do

mdulo

(apenas informativa).

MODULE_AUTHOR(): Autor do mdulo (tambm apenas informativa). MODULE_VERSION(): Verso do mdulo.

Embedded Labworks

EXPORTANDO SMBOLOS

De dentro de um mdulo, apenas um nmero limitado de funes e variveis globais do kernel podem ser acessadas. As funes e variveis precisam ser exportadas explicitamente para serem usadas externamente, e isso pode ser feito atravs de duas macros:

EXPORT_SYMBOL(symbolname), que exporta uma funo ou

varivel para todos os mdulos.

EXPORT_SYMBOL_GPL(symbolname) , que exporta uma funo ou

varivel apenas para mdulos GPL.

Embedded Labworks

COMPILANDO UM MDULO

Existem duas opes para compilar um mdulo:

Fora da rvore do kernel:

Quando o cdigo do mdulo esta fora do diretrio dos fontes do kernel. Fcil de gerenciar, mas no permite compilar um mdulo estaticamente (built-in). Quando o cdigo do mdulo esta dentro do diretrio dos fontes do kernel. Permite compilar um mdulo estaticamente ou dinamicamente.

Dentro da rvore do kernel:

Embedded Labworks

COMPILANDO FORA DA RVORE DO KERNEL


KDIR:=/linux/source/code/directory/ objm+=mymodule.o module: $(MAKE)C$(KDIR)M=$(PWD)modules clean: $(MAKE)C$(KDIR)M=$(PWD)clean

Obs: em caso de compilao cruzada, no esquea de definir os parmetros ARCH e CROSS_COMPILE.

Embedded Labworks

VERSO DO KERNEL

Para ser compilado, um mdulo do kernel precisa acessar os arquivos de cabealho (headers) do kernel. Para isso, temos duas opes:

Usar os fontes completos do kernel. Usar apenas os arquivos de cabealho do kernel.

Um mdulo compilado com uma verso X dos headers do kernel no carregado em um kernel com verso Y.

As ferramentas insmod ou modprobe iro reportar o erro Invalid module format.

Embedded Labworks

INTEGRANDO NOS FONTES DO KERNEL

Para integrar um driver nos fontes do kernel, primeiro adicione o arquivo-fonte em um diretrio apropriado. Exemplo:
drivers/serial/8250.c

Descreva a configurao do driver no arquivo Kconfig disponvel no diretrio onde o fonte do driver foi adicionado:

configSERIAL_8250 tristate"8250/16550andcompatibleserialsupport" selectSERIAL_CORE help Thisselectswhetheryouwanttoincludethedriverforthestandard serialports.ThestandardanswerisY.PeoplewhomightsayN herearethosethataresettingupdedicatedEthernetWWW/FTP servers,orusersthathaveoneofthevariousbusmiceinsteadofa ...

Embedded Labworks

INTEGRANDO NOS FONTES DO KERNEL (cont.)

Adicione uma linha no Makefile baseado na configurao criada no Kconfig:


obj$(CONFIG_SERIAL_8250)+=8250.o

Execute makemenuconfig para habilitar a nova opo. Execute make ou makemodules para compilar. Mais informaes em Documentation/kbuild/.

Embedded Labworks

RECEBENDO PARMETROS
#include<linux/moduleparam.h> /*moduleparametermacro*/ module_param( name, /*nameofanalreadydefinedvariable*/ type, /*eitherbyte,short,ushort,int,uint, long,ulong,charp,orbool. (checkedatcompiletime!)*/ perm /*visibilityinsysfsat /sys/module/<module_name>/parameters/<param> seelinux/stat.h*/ ); /*example*/ intqtd=5; module_param(qtd,int,S_IRUGO);

Embedded Labworks

LABORATRIO

O primeiro mdulo

Embedded Labworks

Linux Device Drivers

Dispositivos de hardware

Embedded Labworks

DISPOSITIVOS

Um papel importante do kernel prover um mecanismo de acesso ao hardware para as bibliotecas e aplicaes. No Linux, o acesso ao hardware exportado para as aplicaes atravs de 3 principais classes de dispositivos:

Character device (dispositivo de caractere). Block Device (dispositivo de bloco). Network device (dispositivo de rede).

Embedded Labworks

CLASSES DE DISPOSITIVOS

Char device: pode ser acessado como um stream contnuo de dados (acesso sequencial), sem comeo, meio e fim. acessado atravs de um arquivo em /dev. Ex: porta serial, impressora, placa de som, etc. Block device: trabalha com blocos de dados, pode ser enderevel, tem comeo, meio e fim. acessado atravs de um arquivo em /dev. Ex: HD, CDROM, DVD, pendrive, etc. Network device: dispositivo totalmente diferente, que pode ser representado por uma interface de rede fsica ou por software (loopback), responsvel por enviar e receber pacotes de dados atravs da camada de rede do kernel. No possui um arquivo em /dev. A comunicao feita atravs de uma API especfica.

Embedded Labworks

ARQUIVOS DE DISPOSITIVO

Os dispositivos de caractere e bloco so representados para as aplicaes atravs de arquivos chamados arquivos de dispositivos e armazenados no diretrio /dev por conveno. Cada arquivo de dispositivo possui 3 informaes bsicas, que identificam internamente o dispositivo ao qual o arquivo pertence:

Tipo (caractere ou bloco). Major number (categoria do dispositivo). Minor number (identificador do dispositivo).

Embedded Labworks

ARQUIVOS DE DISPOSITIVO (cont.)

Exemplos de arquivos de dispositivo:

brwr1rootroot31,0Feb72012/dev/mtdblock0 brwr1rootroot8,1Feb72012/dev/sda1 crwrwrw1rootroot4,64Feb72012/dev/ttyS0 crwrwrw1rootroot4,65Feb72012/dev/ttyS1 crwrwrw1rootroot29,0Feb72012/dev/fb0 crwrwrw1rootroot1,1Feb72012/dev/mem crwrwrw1rootroot1,3Feb72012/dev/null

Embedded Labworks

CONVERSANDO COM O HARDWARE

Como os dispositivos de hardware so exportados atravs de arquivos de dispositivo para as aplicaes, o acesso ao hardware abstrado atravs de uma API comum de acesso arquivos (open, read, write, close). Exemplo de escrita na porta serial:
intfd; fd=open("/dev/ttyS0",O_RDWR); write(fd,"Helloworld!",12); close(fd);

Embedded Labworks

ACESSANDO UM ARQUIVO DE DISPOSITIVO


Aplicao Leitura Escrita User space /dev/ttyS0

major/minor
Kernel space Trata leitura Trata escrita Device driver

Embedded Labworks

CRIANDO ARQUIVOS DE DISPOSITIVO

Os arquivos de dispositivo podem ser criados manualmente: $mknod/dev/<device>[c|b]majorminor Neste caso, preciso conhecer o major/minor number definido no driver do dispositivo, alm de ter previlgios de root. Porm, o mais comum a utilizao de mecanismos automticos de criao dos arquivos de dispositivo:

devtmpfs: virtual filesystem do Linux, disponvel deste a verso 2.6.32. udev: daemon, soluo usada em desktop e servidores. mdev: programa disponvel no Busybox, verso mais leve do udev.

Embedded Labworks

DISPOSITIVOS DE CARACTERE

Com exceo dos drivers para dispositivos de armazenamento, a maioria dos drivers so implementados como um driver de dispositivo de caractere. Portanto, a maioria dos drivers que voc ir desenvolver ser do tipo caractere.

Embedded Labworks

IMPLEMENTANDO UM CHAR DEVICE

Para implementar um driver de dispositivo do tipo caractere, existem trs passos principais:
1. Reservar o major number e o(s) minor number(s) do seu driver. 2. Implementar as operaes que sero disponibilizadas aos usurios do seu driver (open, read, write, close, etc) e associar estas operaes uma estrutura do tipo file_operations. 3. Associar o major e o minor number alocados sua estrutura de operaes de arquivo e registrar o dispositivo de caractere.

Embedded Labworks

DEVICE NUMBER

O primeiro passo para desenvolver um driver de dispositivo de caractere ou bloco registrar um device number para o driver do dispositivo. O device number composto por dois nmeros chamados de major number e minor number. O kernel armazena as informaes de major e minor number no tipo de dados dev_t. O tipo de dados dev_t esta definido em <linux/types.h> e atualmente representado com 32 bits, onde os 12 bits mais significativos representam o major e os 20 bits restantes representam o minor.

Embedded Labworks

TIPO DE DADOS dev_t

Algumas macros so disponibilizadas para gerenciar variveis do tipo dev_t:


/*creatingadevicenumber*/ dev_tmydev=MKDEV(major,minor); /*extractingmajornumber*/ MAJOR(mydev); /*extractingminornumber*/ MINOR(mydev);

Embedded Labworks

REGISTRANDO ESTATICAMENTE

O major number e o minor number podem ser registrados estaticamente ou dinamicamente. Voc estaticamente register_chrdev_region(). pode registrar atravs da funo

Embedded Labworks

FUNO register_chrdev_region()
#include<linux/fs.h> /*allocatedevicenumberstatically*/ intregister_chrdev_region(dev_tfrom, unsignedcount, constchar*name); /*example*/ staticdev_tmydriver_dev=MKDEV(202,128); if(register_chrdev_region(mydriver_dev,4,mydriver)){ pr_err("Failedtoallocatedevicenumber\n"); [...] }

Embedded Labworks

REGISTRANDO DEVICE NUMBER DINMICO

Como voc pode no saber quais dispositivos esto presentes no sistema, pode haver conflito ao tentar alocar estaticamente um major ou minor number j usado por outro dispositivo. Portanto, o melhor alocar dinamicamente com a funo alloc_chrdev_region().

Embedded Labworks

FUNO alloc_chrdev_region()
#include<linux/fs.h> /*allocatedevicenumberdynamically*/ intalloc_chrdev_region(dev_t*dev, unsignedbaseminor, unsignedcount, constchar*name); /*example*/ staticdev_tmydriver_dev; if(alloc_chrdev_region(&mydriver_dev,0,1,"mydriver")){ pr_err("Failedtoallocatedevicenumber\n"); [...] }

Embedded Labworks

REGISTRANDO MAJOR/MINOR (cont.)

Dispositivos registrados so visveis em /proc/devices:


#cat/proc/devices Characterdevices: 1mem 5/dev/tty 13input 14sound [...] Blockdevices: 1ramdisk 7loop 8sd 9md 11sr [...]

Embedded Labworks

IMPLEMENTANDO UM CHAR DEVICE (2)

Para implementar um driver de dispositivo do tipo caractere, existem trs passos principais:
1. Reservar o major number e o(s) minor number(s) do seu driver. 2. Implementar as operaes que sero disponibilizadas aos usurios do seu driver (open, read, write, close, etc) e associar estas operaes uma estrutura do tipo file_operations. 3. Associar o major e o minor number alocados sua estrutura de operaes de arquivo e registrar o dispositivo de caractere.

Embedded Labworks

FILE OPERATIONS

A estrutura file_operations (tambm chamada de fops) contm todas as operaes implementadas pelo device driver. Como ela genrica para todos os arquivos gerenciados pelo kernel, nem todas as operaes definidas nesta estrutura so necessrias para um driver de dispositivo de caractere.

Embedded Labworks

ESTRUTURA file_operations
#include<linux/fs.h> /*mostimportantfileoperationsforachardevice*/ structfile_operations{ [...] ssize_t(*read)(structfile*,char__user*,size_t,loff_t*); ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*); long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong); int(*open)(structinode*,structfile*); int(*release)(structinode*,structfile*); [...] };

Embedded Labworks

FUNO open()
intfoo_open(structinode*i,structfile*f);

Chamada quando o arquivo de dispositivo aberto. A estrutura inode uma representao nica do arquivo no sistema (seja ele um arquivo comum, diretrio, link, dispositivo de caractere ou bloco, etc). Uma estrutura do tipo file criada toda vez que um arquivo aberto.

Armazena informaes como a posio corrente do arquivo, modo de abertura, etc. Tem um ponteiro chamado private_data que pode ser usado livremente pelo driver, j que todas as outras operaes recebem este ponteiro.

Embedded Labworks

FUNO release()
intfoo_release(structinode*i,structfile*f);

Chamada quando o arquivo de dispositivo fechado. As estruturas inode e file so as mesmas da funo open().

Embedded Labworks

FUNO read()
ssize_tfoo_read(structfile*f,__userchar*buf, size_tsz,loff_t*off);

Chamada quando realizada uma operao de leitura no arquivo de dispositivo. O driver dever:

Ler at sz bytes do dispositivo. Salvar os dados lidos no buffer buf. Atualizar a posio atual do arquivo na varivel off. Retornar a quantidade de bytes lidos.

Em sistemas UNIX, a operao de leitura normalmente bloqueia enquanto no houver bytes suficientes para serem lidos do dispositivo.

Embedded Labworks

FUNO write()
ssize_tfoo_write(structfile*f,__userconstchar*buf, size_tsz,loff_t*off);

Chamada quando realizada uma operao de escrita no arquivo de dispositivo. O driver dever:

Ler sz bytes do buffer buf. Escrever os dados lidos no dispositivo. Atualizar a posio atual do arquivo na varivel off. Retornar a quantidade de bytes escritos.

Embedded Labworks

TROCANDO DADOS COM USERSPACE

Um cdigo do kernel no pode acessar diretamente uma regio de memria de espao de usurio, seja desreferenciando um ponteiro ou usando funes do tipo memcpy().

O endereo de memria virtual pode no estar mapeado. Se o endereo passado invlido, acontecer erro de segmentao de memria (segfault) e o kernel matar o processo.

Portanto, para manter o cdigo do driver portvel e seguro, o driver precisa usar algumas funes especficas para trocar dados com o espao de usurio.

Embedded Labworks

FUNES get_user() e put_user()


#include<asm/uaccess.h> /*Readasingleuserspacevariablepointedbyp andsavetokernelvariablev.Return0on successoranegativenumberonerror. */ if(get_user(v,p)){ pr_err("Errorgettingvariable...\n"); [...] } /*Saveasinglekernelvariablevtouserspace variablepointedbyp.Return0onsuccess oranegativenumberonerror. */ if(put_user(v,p)){ pr_err("Errorputtingvariable...\n"); [...] }

Embedded Labworks

FUNO copy_to_user()
#include<asm/uaccess.h> /*Copynbytesfromkernelbuffertouserbuffer. Return0onsuccessoranegativenumberonerror. */ unsignedlongcopy_to_user(void__user*to, constvoid*from, unsignedlongn); /*example*/ if(copy_to_user(user_buf,kernel_buf,qtd)){ pr_err("Errorcopyingtouserbuffer...\n"); [...] }

Embedded Labworks

FUNO copy_from_user()
#include<asm/uaccess.h> /*Copynbytesfromuserbuffertokernelbuffer. Return0onsuccessoranegativenumberonerror. */ unsignedlongcopy_from_user(void*to, constvoid__user*from, unsignedlongn); /*example*/ if(copy_from_user(kernel_buf,user_buf,qtd)){ pr_err("Errorcopyingfromuserbuffer...\n"); [...] }

Embedded Labworks

OUTROS MTODOS

Dependendo da quantidade de dados e do fluxo de comunicao, o uso das funes de troca de dados entre kernel e espao de usurio pode impactar a performance do sistema. Para estes casos, existem solues alternativas onde no necessrio realizar a cpia dos buffers (zero copy):

Implementar a operao mmap() para permitir que um cdigo rodando em espao de usurio tenha acesso direto memria do dispositivo. Usar a funo get_user_pages() para mapear direto uma pgina de memria do usurio ao invs de copi-la.

Embedded Labworks

EXEMPLO read()
staticssize_tmydriver_read(structfile*file,char__user*buf, size_tcount,loff_t*ppos) { intremaining_size,transfer_size; remaining_size=mydriver_bufsize(int)(*ppos); /*checkifEOF*/ if(remaining_size==0){ return0; } /*Sizeofthistransfer*/ transfer_size=min_t(int,remaining_size,count); if(copy_to_user(buf,mydriver_buf+*ppos,transfer_size)){ returnEFAULT; }else{ *ppos+=transfer_size; returntransfer_size; } }

Embedded Labworks

EXEMPLO write()
staticssize_tmydriver_write(structfile*file,constchar__user*buf, size_tcount,loff_t*ppos) { intremaining_bytes; remaining_bytes=mydriver_bufsize(*ppos); if(count>remaining_bytes){ returnEIO; } if(copy_from_user(mydriver_buf+*ppos,buf,count)){ returnEFAULT; }else{ *ppos+=count; returncount; } }

Embedded Labworks

FUNO unlocked_ioctl()
longunlocked_ioctl(structfile*f,unsignedintcmd, unsignedlongarg);

Esta operao esta associada chamada de sistema ioctl(). Permite estender as capacidades do driver alm da API de leitura e escrita em arquivos. Exemplos de utilizao em drivers: configurar o baud rate de uma porta serial, configurar a resoluo de uma placa de vdeo, etc. O comando a ser executado passado no parmetro cmd, e pode-se usar o parmetro arg para passar alguma informao adicional. A semntica dos argumentos cmd e arg especfica de cada driver.

Embedded Labworks

EXEMPLO unlocked_ioctl()
staticlongphantom_ioctl(structfile*file,unsignedintcmd, unsignedlongarg) { structphm_regr; void__user*argp=(void__user*)arg; switch(cmd){ casePHN_SET_REG: if(copy_from_user(&r,argp,sizeof(r))) returnEFAULT; /*dosomething*/ break; casePHN_GET_REG: if(copy_to_user(argp,&r,sizeof(r))) returnEFAULT; /*dosomething*/ break; default: returnENOTTY; } return0; }

Embedded Labworks

EXEMPLO APLICAO COM ioctl()


intmain(void) { intfd,ret; structphm_regreg; fd=open("/dev/phantom"); assert(fd>0); reg.field1=42; reg.field2=67; ret=ioctl(fd,PHN_SET_REG,&reg); assert(ret==0); return0; }

Embedded Labworks

DEFININDO FILE OPERATIONS

Para definir a estrutura de operaes em arquivo, basta declar-la e inicializ-la com os ponteiros para as operaes que voc implementou. Exemplo:
#include<linux/fs.h> staticstructfile_operationsacme_fops= { .owner=THIS_MODULE, .read=acme_read, .write=acme_write, };

Embedded Labworks

IMPLEMENTANDO UM CHAR DEVICE (3)

Para implementar um driver de dispositivo do tipo caractere, existem trs passos principais:
1. Reservar o major number e o(s) minor number(s) do seu driver. 2. Implementar as operaes que sero disponibilizadas aos usurios do seu driver (open, read, write, close, etc) e associar estas operaes uma estrutura do tipo file_operations. 3. Associar o major e o minor number alocados sua estrutura de operaes de arquivo e registrar o dispositivo de caractere.

Embedded Labworks

REGISTRANDO CHAR DEVICE

O kernel representa um dispositivo de caractere com a estrutura cdev. Para registrar um dispositivo de caractere, primeiro declare globalmente uma estrutura do tipo cdev e inicialize-a chamando a funo cdev_init(). Depois s adicionar o dispositivo de caractere ao sistema com a funo cdev_add(). Depois desta chamada, o kernel associar o major/minor number registrado com as operaes em arquivo definidas. Seu dispositivo esta pronto para ser usado!

Embedded Labworks

EXEMPLO CRIANDO CHAR DEVICE


#include<linux/cdev.h> staticstructcdevmydriver_cdev; staticint__initmydriver_init(void) { [...] cdev_init(&mydriver_cdev,&mydriver_fops); if(cdev_add(&mydriver_cdev,mydriver_dev,1)){ pr_err("Chardriverregistrationfailed\n"); [...] } [...] }

Embedded Labworks

DESREGISTRANDO

Na funo de limpeza do driver necessrio desregistrar o dispositivo de caractere. Para isso, primeiro remova o dispositivo de caractere com a funo cdev_del(). E depois libere o major/minor number alocado com a funo unregister_chrdev_region().

Embedded Labworks

EXEMPLO DESREGISTRANDO CHAR DEVICE


#include<linux/cdev.h> staticvoid__exitmydriver_exit(void) { [...] cdev_del(&mydriver_cdev); unregister_chrdev_region(mydriver_dev,1); [...] }

Embedded Labworks

CDIGOS DE ERRO DO LINUX

A conveno do Linux para tratamento de erros a seguinte:

Retornar 0 no caso de sucesso. Retornar um nmero negativo no caso de erro.

Regra geral: sempre trate o retorno das funes e retorne um cdigo de erro compatvel com o problema apresentado. Os cdigos de erro padro esto disponveis em:
asmgeneric/errnobase.h asmgeneric/errno.h

Embedded Labworks

SUMRIO

Defina e implemente as operaes de arquivo do driver (open, read, write, close, ioctl, etc). Declare a estrutura file_operations e inicialize-a com os ponteiros das operaes de arquivo implementadas. Na funo de inicializao do mdulo, reserve o major/minor number com a funo register_chrdev_region(), inicialize a estrutura cdev com a funo cdev_init() e adicione no sistema com cdev_add(). Na funo de limpeza do mdulo, desregistre o dispositivo de caractere com dev_del() e depois desregistre o major/minor number com a funo unregister_chrdev_region().

Embedded Labworks

LABORATRIO

O primeiro driver

Embedded Labworks

DISPOSITIVOS NO KERNEL

Durante os testes do driver desenvolvido, foi necessrio criar o arquivo de dispositivo manualmente. Como tornar a criao de um arquivo de dispositivo automtica? Temos duas opes:

Delegar para o kernel a criao dos arquivos de dispositivo. Isso possvel atravs do devtmpfs. Fazer com que o kernel gere um evento toda vez que um dispositivo de hardware for registrado no sistema. Uma aplicao como o udev ou o mdev pode capturar estes eventos e cuidar da criao dos arquivos de dispositivo.

Embedded Labworks

CRIANDO ARQUIVOS DE DISPOSITIVO


1

Kernel space

devtmpfs

Dispositivo identificado
2

User space

/dev

User space daemon

Embedded Labworks

DEVICE MODEL

Para ambos os casos funcionarem, o dispositivo precisa fazer parte do modelo de dispositivos (device model) do kernel. O device model prov um mecanismo nico para representar os dispositivos de hardware e sua topologia no sistema. O device model nada mais do que um conjunto de objetos do kernel, chamados internamente de kobjects. Estes objetos so conectados entre si, e formam uma hierarquia de dispositivos e barramentos conectados ao sistema.

Embedded Labworks

SYSFS

O sysfs basicamente um sistema de arquivo virtual que exporta esta hierarquia de objetos para user space. Por este motivo, no sysfs podemos colher informaes sobre os dispositivos de hardware conectados ao sistema. montado normalmente normalmente no diretrio /sys:
$mounttsysfsnone/sys

Embedded Labworks

SYSFS (cont.)

Estes so alguns de seus principais diretrios:

devices: Hierarquia completa dos dispositivos conectados ao

sistema.

bus: Dispositivos classificados por barramento (spi, pci, usb, etc). class: Dispositivos classificados por tipo (net, input, block, etc). dev: Dispositivos classificados por major e minor number.

Mais detalhes em Documentation/filesystems/sysfs.txt .

Embedded Labworks

REGISTRANDO O DISPOSITIVO

Para registrar um dispositivo no device model do kernel, precisamos primeiro criar uma classe com a funo class_create() e depois registrar o dispositivo com a funo device_create(). Da mesma forma, para desregistrar o dispositivo precisamos remov-lo com a funo device_destroy() e depois destruir a classe com a funo class_destroy().

Embedded Labworks

DEVICE API
#include<linux/device.h> /** *class_createcreateastructclassstructure */ structclass*class_create(structmodule*owner, constchar*name); /** *device_createcreatesadeviceandregistersit *withsysfs */ structdevice*device_create(structclass*class, structdevice*parent, dev_tdevt,void*drvdata, constchar*fmt,...);

Embedded Labworks

DEVICE API (cont.)


#include<linux/device.h> /** *class_destroydestroysastructclassstructure */ voidclass_destroy(structclass*cls) /** *device_destroyremovesadevicethatwascreatedwith device_create() */ voiddevice_destroy(structclass*class,dev_tdevt);

Embedded Labworks

DEVICE API (EXEMPLO)


#include<linux/device.h> staticstructclass*cosa_class; staticint__initcosa_init(void) { structdevice*dev; cosa_class=class_create(THIS_MODULE,"cosa"); dev=device_create(cosa_class,NULL, MKDEV(cosa_major,0),NULL,"cosa%d",0); if(IS_ERR(dev)){ //handleerror } [...] }

Embedded Labworks

DEVICE API
#include<linux/device.h> staticstructclass*cosa_class; staticvoid__exitcosa_exit(void) { [...] device_destroy(cosa_class,MKDEV(cosa_major,0)); class_destroy(cosa_class); [...] }

Embedded Labworks

UEVENT

Na chamada device_create() ou qualquer outra funo que altere a hierarquia de dispositivos conectados ao sistema, o kernel ir gerar um evento, chamado de uevent. Este evento comunicado para user space atravs de um netlink socket (socket multicast especfico para comunicao entre kernel e user space). Um daemon rodando em user space pode escutar estes eventos e trat-los. isso que faz o udevd (daemon do udev).

Embedded Labworks

UDEV

O udev o gerenciador padro de dispositivos do Linux, presente na maioria das distribuies. composto por um conjunto de ferramentas e daemons como o udevd e o udevinfo. Seu comportamento pode ser configurvel atravs de um conjunto de regras armazenadas em /etc/udev/rules.d/.

Embedded Labworks

HOTPLUG
Kernel space
device_create()

Device Driver

Registra dispositivo uevent


Conexo via netlink socket

User space

/dev

udevd

Consulta regras

Embedded Labworks

COLDPLUG

Mas o que acontece com os dispositivos que foram identificados e registrados no boot do sistema, quando o daemon do udev ainda no estava rodando? a que o sysfs tem um papel importante na soluo. Durante o boot, o kernel cria um arquivo chamado uevent para cada dispositivo criado no sistema. O udev varre o sysfs procurando por estes arquivos e gera os eventos correspondentes, como se eles estivessem acontecendo naquele momento.

Embedded Labworks

OUTRAS FUNCIONALIDADES DO UDEV

Alm da capacidade de criar os arquivos de dispositivo no /dev, o udev tem ainda outras funcionalidades:

Diferenciar dispositivos e executar qualquer comando, script ou aplicao para determinado dispositivo. Carregar firmware. Carregar mdulos do kernel (para dispositivos conectados em barramentos que suportam hotplug).

Embedded Labworks

MDEV

O mdev uma implementao de gerenciador de dispositivos mais leve presente no Busybox. Tem os mesmos objetivos do udev, porm menos flexvel. Ele capaz de:

Criar dinamicamente arquivos de dispositivo. Executar comandos especficos para cada dispositivo conectado. Realizar a carga de firmware.

Seu arquivo de configurao fica em /etc/mdev.conf.

Embedded Labworks

DEVTMPFS

O devtmpfs um sistema de arquivo virtual que permite delegar ao kernel o gerenciamento dos arquivos de dispositivo. Esta funcionalidade habilitada na opo CONFIG_DEVTMPFS. Com esta opo habilitada, s montar no /dev o sistema de arquivo devtmpfs:
$mounttdevtmpfsnone/dev

Para o kernel montar este sistema de arquivo automaticamente no boot, habilite a opo CONFIG_DEVTMPFS_MOUNT.

Embedded Labworks

MISC DRIVERS

Misc (miscellaneous) drivers so drivers de dispositivo de caractere que compartilham algumas caractersticas em comum. O kernel abstrai estas caractersticas comuns em uma API (implementao em drivers/char/misc.c). Todos os misc drivers possuem o major number 10, e cada um tem seu minor number. Quando usar? Drivers de dispositivo de caractere bem simples, que precisam de apenas um minor number. Por exemplo, muitos drivers de watchdog (em drivers/watchdog) so implementados atravs de misc drivers.

Embedded Labworks

MISC DRIVER (cont.)

Vimos que, em um dispositivo de caractere, comum realizarmos as seguintes etapas:

Alocar o major/minor number, usando por exemplo a funo alloc_chrdev_region(). Criar o dispositivo de caractere com as funes cdev_init() e cdev_add(). Registrar o dispositivo de caractere no device model com as funes class_create() e device_create().

Um misc driver substitui todo este processo apenas pela chamada funo misc_register().

Embedded Labworks

MISC DRIVER API


#include<linux/miscdevice.h> structmiscdevice{ intminor; constchar*name; conststructfile_operations*fops; [...] }; /** *misc_registerregisteramiscellaneousdevice */ intmisc_register(structmiscdevice*misc);

Embedded Labworks

CHAR DRIVER
staticint__initbtn_init(void) { [...] result=alloc_chrdev_region(&btn_dev,0,1,DEVICE_NAME); [...] cdev_init(&btn_cdev,&btn_fops); result=cdev_add(&btn_cdev,btn_dev,1); [...] btn_class=class_create(THIS_MODULE,DEVICE_NAME); device_create(btn_class,NULL,MKDEV(MAJOR(btn_dev),0), "btn"); [...] }

Embedded Labworks

MISC DRIVER
#include<linux/miscdevice.h> staticstructmiscdevicebtn_dev={ MISC_DYNAMIC_MINOR, "btn", &btn_fops }; staticint__initbtn_init(void) { [...] result=misc_register(&btn_dev); [...] }

Embedded Labworks

LABORATRIO

Registrando o dispositivo

Embedded Labworks

Linux Device Drivers

Gerenciamento de memria

Embedded Labworks

MEMRIA

O desenvolvimento de alguns tipos de drivers, principalmente aqueles que precisam de performance, requerem um conhecimento maior de como o sub-sistema de memria virtual do Linux funciona. O sub-sistema de memria virtual no Linux fortemente apoiado em componente de hardware chamado MMU. A MMU (Memory Management Unit) o hardware que implementa o mecanismo de memria virtual, gerenciando a memria do sistema e fazendo a converso entre endereos de memria fsicos e virtuais.

Embedded Labworks

MEMORY MANAGEMENT UNIT

CPU

Endereo virtual

MMU

Endereo fsico

Memria

Embedded Labworks

VANTAGENS DA MMU

Um sistema com MMU capaz de prover:

Maior endereamento de memria para os processos: em uma arquitetura de 32 bits, os processos podem ter acesso um endereamento linear de at 3G de memria virtual. Proteo: cada processo s enxerga seu espao de endereamento, onde um acesso invlido gera uma exceo. Memory mapping: possibilidade de mapear um arquivo fsico em memria. Compartilhamento: os processos podem compartilhar memria (cdigo, dados, etc), usado por exemplo em mecanismos de IPC. SWAP: se faltar memria fsica, possibilita salvar e recuperar pginas de memria do disco.

Embedded Labworks

COMO FUNCIONA NO LINUX?

O kernel divide o espao de endereamento de memria virtual em duas partes:

Endereamento de memria virtual do kernel. Endereamento de memria virtual dos processos.

Esta diviso configurada em tempo de compilao no kernel em MemorySplit, com trs opes 1G/3G, 2G/2G e 3G/1G. Por exemplo, um sistema de 32 bits consegue enderear at 4G de memria virtual. Se configurado com 3G/1G, temos 1G de memria enderevel pelo kernel e 3G de memria enderevel pelos processos.

Embedded Labworks

ORGANIZAO DA MEMRIA 3G/1G (x86)


0xFFFFFFFF

Kernel

1GB do endereamento de memria virtual reservado para o kernel, contendo o cdigo do kernel e suas estruturas de dados principais. 3GB do endereamento de memria virtual reservado para cada processo, para armazenar cdigo, dados (stack, heap, etc), arquivos mapeados em memria, etc. No necessariamente um endereo virtual reservado para um processo pode estar mapeado para um endereo fsico!

0xC0000000

Processo 1

0x00000000

Embedded Labworks

ORGANIZAO DA MEMRIA 2G/2G (i.MX53)


0xFFFFFFFF

Kernel

2GB do endereamento de memria virtual reservado para o kernel, contendo o cdigo do kernel e suas estruturas de dados principais. 2GB do endereamento de memria virtual reservado para cada processo, para armazenar cdigo, dados (stack, heap, etc), arquivos mapeados em memria, etc. No necessariamente um endereo virtual reservado para um processo pode estar mapeado para um endereo fsico!

0x80000000

Processo 1

0x00000000

Embedded Labworks

Endereamento Virtual de Memria


0xFFFFFFFF

Endereamento Fsico de Memria

Kernel
0xC0000000

Processo 1
0x00000000

RAM

CPU
0xFFFFFFFF

MMU I/O

Kernel
0xC0000000

Processo 2
0x00000000

Flash

Embedded Labworks

MEMRIA FSICA

Apesar de a menor unidade de memria enderevel pela CPU seja um byte ou uma palavra (word), o kernel divide a memria fsica em pginas de memria. Isso porque, para gerenciar a memria e realizar a converso de endereos virtuais para endereos fsicos, a MMU divide a memria em pginas e mantm uma tabela de pginas de memria do sistema. O tamanho de uma pgina de memria pode variar dependendo da arquitetura (tipicamente 4K em arquiteturas de 32 bits e 8K em arquiteturas de 64 bits). Isso significa que, em um sistema de 32 bits, com pginas de 4K e 1G de memria fsica, teremos 262.144 pginas fsicas de memria.

Embedded Labworks

ZONAS DE MEMRIA

O kernel divide as pginas de memria fsica em diferentes zonas:

ZONE_DMA e ZONE_DMA32: pginas de memria para operaes de DMA. ZONE_NORMAL: pginas de memria mapeadas normalmente no espao do kernel. ZONE_HIGHMEM: pginas de memria que no podem ser mapeadas no espao de endereamento do kernel. So alocadas dinamicamente, conforme a necessidade.

Desta forma, quando por exemplo um driver precisa alocar memria para realizar uma operao de DMA, ele ir alocar de ZONE_DMA. O uso e o layout das zonas de memria so totalmente dependentes de arquitetura.

Embedded Labworks

EXEMPLO DE MAPEAMENTO EM 32 BITS


Espao de endereamento virtual (4G)
0xFFFFFFFF

Espao de endereamento fsico (2G)

Kernel

0xC0000000 0x80000000

ZONE_HIGHMEM Processo 1 ZONE_NORMAL ZONE_DMA


0x00000000 0x00000000

Embedded Labworks

MEMRIA PARA OS PROCESSOS

A memria para os processos alocada das zonas ZONE_HIGHMEM (se existente) ou ZONE_NORMAL. Durante uma alocao de memria para um processo, o kernel no necessariamente aloca fisicamente esta pgina para o processo. Neste caso, o kernel pode usar uma funcionalidade chamada demand fault paging para alocar dinamicamente uma pgina de memria, atravs da exceo page fault gerada pela MMU quando o processo tentar acessar esta regio de memria.

Embedded Labworks

MEMRIA PARA OS PROCESSOS (cont.)

permitido uma aplicao alocar mais memria do que a disponvel fisicamente no sistema (mas pode levar falta de memria). Neste caso, o OOM Killer (Out of Memory Killer) entra em ao e seleciona um processo para matar e ganhar um pouco mais de memria (melhor do que travar!)

Embedded Labworks

Linux Device Drivers

Alocao de memria

Embedded Labworks

ALOCANDO MEMRIA

Existem basicamente quatro mecanismos de alocao de memria no kernel:

Pageallocator: funes de baixo nvel que permitem alocaes na

granularidade de pginas de memria (normalmente 4KB em 32 bits).

SLABAllocator: usa as funes do page alocator para criar caches

de alocao de memria, permitindo alocar objetos menores que uma pgina de memria.

vmalloc(): tambm usa as funes do page allocator, mas permite

alocar regies no-contnuas de memria fsica.

kmallok(): mecanismo mais comum que usa as funes do SLAB

allocator para alocar memria.

Embedded Labworks

Cdigo do kernel

kmalloc()

vmalloc()

SLAB Allocator

Page allocator

Embedded Labworks

PAGE ALLOCATOR

Apropriado para alocaes de grandes regies de memria (maiores que 128K). Trabalha com alocao de pginas de memria (normalmente 4K em 32 bits) e trabalha apenas em potncia de 2 (1 pgina, 2 pginas, 4 pginas, 8 pginas, etc). Aloca no mximo 8MB ( possvel mudar este valor na configurao do kernel). Aloca uma regio de memria virtual contnua, e tambm fisicamente contnua (o que pode ser um problema se a memria estiver muito fragmentada).

Embedded Labworks

PAGE ALLOCATOR API


#include<linux/gfp.h> /*allocatephysicalpages*/ unsignedlong__get_free_page(intflags); unsignedlongget_zeroed_page(intflags); unsignedlong__get_free_pages(intflags,unsignedintorder); /*freeallocatedpages*/ voidfree_page(unsignedlongaddr); voidfree_pages(unsignedlongaddr,unsignedintorder);

Embedded Labworks

FLAGS

Estas so as mais comuns flags em alocao de memria:

GFP_KERNEL: Flag padro para alocao de memria, usado na

maioria das situaes.

GFP_ATOMIC: Usada em situaes onde no se pode dormir (sees

crticas ou rotinas de tratamento de interrupo).

GFP_DMA: Usada quando necessrio alocar memria da zona de

DMA.

As outras flags esto definidas em <linux/gfp.h>.

Embedded Labworks

SLAB ALLOCATOR

Camada que usa a API da page allocator para criar um cache de alocao de memria, permitindo alocar objetos menores que o tamanho de uma pgina de memria. Existem trs implementaes da camada SLAB (todas implementam a mesma API):

SLAB: verso original. SLOB: mais simples, economiza espao, mas no escala bem. SLUB: mecanismo padro a partir da verso 2.6.23. Simples e escala melhor que o SLAB, gerando menos fragmentao de memria.

Sua API usada internamente no kernel (vide <linux/slab.h>), mas dificilmente ser usada em um driver comum.

Embedded Labworks

KMALLOC

Em um driver comum, o mecanismo padro de alocao atravs da funo kmalloc(). Permite objetos de 8 bytes a 128KB /proc/slabinfo,necessrio habilitar SLUB_DEBUG). alocar (vide

A rea alocada ser fisicamente contnua e arredondada potncia de 2. Usa os mesmos flags do page allocator (GFP_KERNEL, GFP_ATOMIC, GFP_DMA, etc), e com a mesma semntica.

Embedded Labworks

KMALLOC API
#include<linux/slab.h> /*allocatesizebytesandreturnapointer tothearea(virtualaddress)*/ void*kmalloc(size_tsize,intflags); /*freeanallocatedarea*/ voidkfree(constvoid*objp); /*example*/ structib_update_work*work; work=kmalloc(sizeof*work,GFP_ATOMIC); [...] kfree(work);

Embedded Labworks

KMALLOC API (cont.)


#include<linux/slab.h> /*allocatesazeroinitializedbuffer*/ void*kzalloc(size_tsize,gfp_tflags); /*allocatesmemoryforanarrayofnelementsofsize size,andzeroesitscontents*/ void*kcalloc(size_tn,size_tsize,gfp_tflags); /*changesthesizeofthebufferpointedbyptonew_size, byreallocatinganewbufferandcopyingthedata, unlessthenew_sizefitswithinthealignmentofthe existingbuffer.*/ void*krealloc(constvoid*p,size_tnew_size,gfp_tflags);

Embedded Labworks

VMALLOC ALLOCATOR

Permite alocar um endereo de memria virtual contnuo, mas no fisicamente contnuo. Permite alocaes de grandes reas de memria j que no sofre de problemas de fragmentao, mas no pode ser usado para regies de memria de DMA, que requer uma regio de memria fisicamente contnua.

Embedded Labworks

VMALLOC ALLOCATOR API


#include<linux/vmalloc.h> /*allocatesizebytesandreturnsavirtualaddress*/ void*vmalloc(unsignedlongsize); /*freeallocatedmemory*/ voidvfree(void*addr);

Embedded Labworks

KERNEL MEMORY DEBUGGING

KMEMCHECK:

verifica dinamicamente por memria no inicializada, apenas disponvel no x86 (32/64 bits). Documentao em:
Documentation/kmemcheck.txt DEBUG_KMEMLEAK:

verifica dinamicamente por memory leaks, disponvel em todas as arquiteturas. Documentao em:
Documentation/kmemleak.txt

Estas funcionalidades adicionam um overhead na execuo do kernel. Use apenas em desenvolvimento!

Embedded Labworks

Linux Device Drivers

Hardware I/O

Embedded Labworks

ACESSANDO I/O

Existem basicamente dois mecanismos de acesso I/O, de acordo com a arquitetura da CPU:

Memory-mapped I/O: quando a CPU utiliza o mesmo barramento de endereos para enderear memria e I/O. Neste caso, o acesso realizado normalmente atravs de instrues de acesso memria. o mecanismo mais usado por diferentes arquiteturas suportadas pelo Linux. Port I/O: quando a CPU utiliza barramentos de endereos diferentes para acessar memria e I/O. Neste caso, o acesso I/O realizado atravs do uso de instrues especiais da CPU (ex: instrues IN e OUT na arquitetura x86).

Embedded Labworks

PORT I/O

Antes de usar um port I/O, o driver deve requisitar ao kernel a regio de I/O que deseja usar com a funo request_region(). Voc pode listar todos os I/Os registrados pelos drivers no arquivo /proc/ioports. Isso evita que outros drivers usem a mesma regio de I/O (mas um procedimento puramente voluntrio). Depois de usada, a regio de I/O deve ser liberada com a funo release_region().

Embedded Labworks

PORT I/O API


#include<linux/ioport.h> /*requestI/Oregion*/ structresource*request_region(unsignedlongstart, unsignedlonglen, char*name); /*releaseI/Oregion*/ voidrelease_region(unsignedlongstart,unsignedlonglen);

Embedded Labworks

PORT I/O API


#include<linux/ioport.h> /*example:requestingI/Oport*/ if(request_region(0x03f8,8,"serial")==NULL){ pr_err("FailedrequestingI/Oregion!\n"); [...] } /*example:releasingI/Oport*/ release_region(0x0170,8);

Embedded Labworks

PORT I/O API (cont.)


#include<asm/io.h> /*read/writebytes(8bits)*/ unsignedinb(unsignedlong*addr); voidoutb(unsignedport,unsignedlong*addr); /*read/writewords(16bits)*/ unsignedinw(unsignedlong*addr); voidoutw(unsignedport,unsignedlong*addr); /*read/writelongs(32bits)*/ unsignedinl(unsignedlong*addr); voidoutl(unsignedport,unsignedlong*addr);

Embedded Labworks

PORT I/O API (cont.)


#include<asm/io.h> /*read/writemultiplebytes(8bits)*/ voidinsb(unsignedport,void*addr,unsignedlongcount); voidoutsb(unsignedport,void*addr,unsignedlongcount); /*read/writemultiplewords(16bits)*/ voidinsw(unsignedport,void*addr,unsignedlongcount); voidoutsw(unsignedport,void*addr,unsignedlongcount); /*read/writemultiplelongs(32bits)*/ voidinsl(unsignedport,void*addr,unsignedlongcount); voidoutsl(unsignedport,void*addr,unsignedlongcount);

Embedded Labworks

PORT I/O API (cont.)


#include<asm/io.h> /*example:read8bits*/ oldlcr=inb(baseio+UART_LCR); /*example:write8bits*/ outb(MOXA_MUST_ENTER_ENCHANCE,baseio+UART_LCR);

Embedded Labworks

MEMORY-MAPPED I/O

O primeiro passo para o uso de memory-mapped I/O requisitar a regio de I/O para o kernel atravs da funo request_mem_region(), normalmente chamada na inicializao do device driver. Esta funo ir registrar o uso de determinada regio de I/O mapeada em memria. Em /proc/iomem temos todas as regies de I/O mapeadas em memria j reservadas no kernel. Ao driver, devemos chamar a funo release_mem_region() para notificar o kernel de que esta regio de I/O mapeado em memria esta livre para uso. descarregar o

Embedded Labworks

MEMORY-MAPPED I/O API


#include<linux/ioport.h> /*requestmemorymappedI/O*/ structresource*request_mem_region(unsignedlongstart, unsignedlonglen, char*name); /*releasememorymappedI/O*/ voidrelease_mem_region(unsignedlongstart, unsignedlonglen);

Embedded Labworks

MEMORY-MAPPED I/O API (cont.)


#include<linux/ioport.h> /*example:requestingmemorymappedI/O*/ if(request_mem_region(TB0219_START,TB0219_SIZE, "TB0219")==NULL){ pr_err("FailedrequestingmemorymappedI/Oregion!\n"); [...] } /*example:releasingmemorymappedI/O*/ release_mem_region(TB0219_START,TB0219_SIZE);

Embedded Labworks

MEMORY-MAPPED I/O NA MEMRIA VIRTUAL

O kernel s trabalha com memria virtual! Portanto, para acessar um endereo de memria correspondente determinado I/O, precisamos converter o endereo fsico desta porta de I/O em um endereo virtual antes de us-lo. Isso pode ser feito com a funo ioremap().

Embedded Labworks

MEMORY-MAPPED I/O API


#include<asm/io.h> /*getmemorymappedI/Ovirtualaddress*/ void*ioremap(unsignedlongphys_addr,unsignedlongsize); /*unmapmemorymappedI/Ovirtualaddress*/ voidiounmap(void*address);

Embedded Labworks

MEMORY-MAPPED I/O API (cont.)


#include<asm/io.h> /*example:mappingmemorymappedI/Oaddress*/ staticvoid__iomem*tb0219_base; if((tb0219_base=ioremap(TB0219_START,TB0219_SIZE))==NULL){ pr_err("FailedremappingmemorymappedI/O!\n"); [...] } /*example:unmappingmemorymappedI/Oaddress*/ iounmap(tb0219_base);

Embedded Labworks

MEMORY-MAPPED I/O API (cont.)


#include<asm/io.h> /*read/writebytes(littleendianaccess)*/ unsignedreadb(void*addr); voidwriteb(unsignedval,void*addr); /*read/writewords(littleendianaccess)*/ unsignedreadw(void*addr); voidwritew(unsignedval,void*addr); /*read/writelongs(littleendianaccess)*/ unsignedreadl(void*addr); voidwritel(unsignedval,void*addr);

Embedded Labworks

MEMORY-MAPPED I/O API (cont.)


#include<asm/io.h> /*read/writebytes(rawaccess)*/ unsigned__raw_readb(void*addr); void__raw_writeb(unsignedval,void*addr); /*read/writewords(rawaccess)*/ unsigned__raw_readw(void*addr); void__raw_writew(unsignedval,void*addr); /*read/writelongs(rawaccess)*/ unsigned__raw_readl(void*addr); void__raw_writel(unsignedval,void*addr);

Embedded Labworks

MEMORY-MAPPED I/O API (cont.)


#include<asm/io.h> /*example:littleendianbyteread*/ tmp8=readb(mmio+CARM_INITC); /*example:littleendianlongwrite*/ writel(data,lynx>registers+offset); /*example:rawbyteread*/ charsense=__raw_readb(GDROM_ERROR_REG); /*example:rawlongwrite*/ __raw_writel(1<<KS8695_IRQ_UART_TX, membase+KS8695_INTST);

Embedded Labworks

NOVA API DE ACESSO I/O

Existe uma nova API para acessar dispositivos de I/O de forma transparente, seja port I/O ou memory-mapped I/O. Alguns drivers usam esta funcionalidade, mas parece no existir ainda um consenso se todos os drivers devem ser desenvolvidos/migrados para este novo mecanismo.

Embedded Labworks

NEW I/O API


#include<asm/io.h> /*mappingportI/O*/ void*ioport_map(unsignedlongport, unsignedintcount); voidioport_unmap(void*addr); /*mappingmemorymappedI/O*/ void*ioremap(unsignedlongphys_addr, unsignedlongsize); voidiounmap(void*addr);

Embedded Labworks

NEW I/O API (cont.)


#include<asm/io.h> /*read/write8bits*/ unsignedintioread8(void*addr); voidiowrite8(u8value,void*addr); /*read/write16bits*/ unsignedintioread16(void*addr); voidiowrite16(u16value,void*addr); /*read/write32bits*/ unsignedintioread32(void*addr); voidiowrite32(u32value,void*addr);

Embedded Labworks

NEW I/O API (cont.)


#include<asm/io.h> /*setvaluestartingonaddrcounttimes*/ voidmemset_io(void*addr,u8value,unsignedintcount); /*copyfromI/Otomemorycountbytes*/ voidmemcpy_fromio(void*dest,void*source, unsignedintcount); /*copyfrommemorytoI/Ocountbytes*/ voidmemcpy_toio(void*dest,void*source, unsignedintcount);

Embedded Labworks

MEMORY BARRIERS

Imagine que, para acessar um byte de uma E2PROM, voc precise configurar o endereo que deseja acessar no registrador A e depois fazer a leitura no registrador B. Para ler o byte do endereo 8 ficaria assim:
A=8;//setaddress x=B;//readbyte

Neste exemplo, a ordem de execuo das instrues extremamente importante, mas para a CPU ou para o compilador, elas parecem ser totalmente independentes e podem ser executadas em qualquer ordem!

Embedded Labworks

MEMORY BARRIERS (cont.)

Portanto, o compilador ou a CPU podem reordenar a execuo das instrues ou o acesso memria (desde que no influenciem a operao aparente do programa em execuo). Existe uma tcnica chamada Memory Barriers para previnir este tipo de problema.
A=5;//setaddress insert_barrier();//insertmemorybarrier x=B;//readbyte

Embedded Labworks

MEMORY BARRIERS (cont.)

A API do kernel prov algumas funes que funcionam como memory barriers:

barrier(): previne otimizaes do compilador, mas no influencia

no funcionamento do hardware.

rmb(): memory barrier para leitura. wmb(): memory barrier para escrita. mb(): memory barrier para leitura e escrita.

Embedded Labworks

MEMORY BARRIERS (cont.)


#include<asm/system.h> #include<asm/io.h> /*example*/ __raw_writel(0x01,DIRECTION_ADDR); wmb(); status=__raw_readl(STATUS_ADDR);

Embedded Labworks

MEMORY BARRIERS (cont.)

Mecanismos de sincronizao do kernel como spinlocks tambm server como memory barriers (veremos spinlocks mais adiante). Mais detalhes na documentao do kernel:
Documentation/memorybarriers.txt

Embedded Labworks

LABORATRIO

Acessando o hardware

Embedded Labworks

Linux Device Drivers

Gerenciamento de processos

Embedded Labworks

PROCESSOS E THREADS

Um processo um programa em execuo. Em sistemas Unix, um processo criado atravs da chamada de sistema fork(). Um processo composto por:

Um espao de endereamento virtual, que contm cdigo, dados, stack, bibliotecas carregadas dinamicamente, etc. Uma thread, cujo ponto de entrada a funo main().

Embedded Labworks

PROCESSOS E THREADS (cont.)

Dentro de um processo, threads adicionais podem ser criadas com a funo pthread_create():

As threads compartilham o mesmo espao de endereamento virtual do processo. O ponto de entrada da thread passado como argumento em pthread_create().

Embedded Labworks

PROCESSOS E THREADS NO KERNEL

Internamente, o kernel no diferencia processos e threads. Uma thread nada mais do que um processo que compartilha dados com outros processos. Por este motivo, o Linux escalona threads e no processos! A partir de agora, pelo fato do kernel tratar tudo como tarefa (task), vamos nos referir thread ou processo apenas como tarefa.

Embedded Labworks

Processo 1 Thread 1 Thread 2 Thread 3

Processo 2 Thread 1

Processo 3 Thread 1 Thread 2

User space

task_struct
Tarefa 1 Tarefa 2 Tarefa 3 Tarefa 4 Tarefa 5 Tarefa 6

Kernel space

Embedded Labworks

OS ESTADOS DE UMA TAREFA

TASK_RUNNING: O processo esta em execuo ou pronto para ser executado. TASK_INTERRUPTIBLE: O processo esta bloqueado esperando alguma condio, mas pode ser interrompido se receber um sinal. TASK_UNINTERRUPTIBLE: O processo esta bloqueado esperando alguma condio, mas ignora qualquer sinal. EXIT_ZOMBIE: O processo terminou mas o processo-pai ainda no fez a limpeza. __TASK_TRACED: O processo esta sendo restreado por outro processo (tracing) via chamada de sistema ptrace(). __TASK_STOPPED: O processo parou sua execuo porque recebeu algum sinal (SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU) via debugger ou controle de job.

Embedded Labworks

fork() ou pthread_create()

TAREFA CRIADA

EXIT_ZOMBIE
Finalizada, aguardando ACK do processo-pai
Tarefa selecionada pelo escalonador

TASK_RUNNING
Pronta aguardando execuo

TASK_RUNNING
Em execuo
Tarefa interrompida pelo escalonador Para executar outra tarefa

Evento acontece e tarefa fica pronta para execuo

TASK_INTERRUPTIBLE ou TASK_UNINTERRUPTIBLE
Bloqueada

Tarefa bloqueada esperando algum evento

Embedded Labworks

O ESCALONADOR

Como e quando escalonar uma tarefa? Trade-off entre capacidade de processamento e tempo de resposta (latncia). O Linux um sistema multitasking preemptivo. Possui o conceito de classes de escalonador, onde cada classe possui um algoritmo de escalonamento, que decide qual processo deve ser executado, quando e por quanto tempo. O escalonador padro do Linux o CFS (Completely Fair Scheduler), onde cada processo recebe uma "porcentagem justa" da CPU (foco em performance).

Embedded Labworks

ESCALONANDO TAREFAS

O kernel pode interromper uma tarefa em execuo tanto em espao de usurio (padro) quanto em espao de kernel (configurvel via opo CONFIG_PREEMPT). Existem diversos pontos onde o kernel pode interromper uma tarefa em execuo para executar outra tarefa, incluindo:

Ao fim do time slice (quantum) da tarefa. No retorno do tratamento de uma interrupo. No retorno de uma chamada de sistema.

Embedded Labworks

TAREFAS DE TEMPO REAL

O Linux possui tambm um escalonador para processos de tempo real, que tem prioridade sobre o CFS. Mas mesmo assim, o Linux no pode ser considerado um sistema operacional determinstico (em alguns trechos do cdigo a latncia para atender um pedido de interrupo pode ser muito grande). Existe um conjunto de patches (PREEMPT_RT) que pode ser aplicado ao kernel e melhorar este cenrio de alta latncia. Uma opo para o uso do Linux em aplicaes hard real-time a utilizao de um kernel de tempo real em conjunto com o Linux (RTLinux, RTAI, Xenomai).

Embedded Labworks

CONTEXTO DE EXECUO

Uma tarefa pode rodar tanto em espao de usurio (user space) quanto em espao de kernel (kernel space). Quando uma tarefa rodando em espao de usurio (user space) executa uma chamada de sistema, ela entra em espao de kernel (kernel space). Porm, apesar de estar executando em kernel space, a chamada de sistema executada no contexto da tarefa que a requisitou. Aps finalizar a execuo da chamada de sistema, o kernel pode retornar para a execuo da tarefa em espao de usurio ou escalonar uma outra tarefa para execuo.

Embedded Labworks

KERNEL THREADS

Kernel threads so tarefas comuns, mas que so executadas em kernel space. Muito til para o kernel ou algum device driver executar operaes em background. As threads do kernel aparecem na listagem do programa ps entre colchetes. A API para a criao de threads do kernel esta disponvel em <linux/kthread.h>.

Embedded Labworks

KERNEL THREADS (cont.)

Voc pode usar a funo kthread_create() para criar uma thread do kernel. Neste caso a thread criada de forma bloqueada, e voc precisa acord-la com a funo wake_up_process(). Voc pode tambm usar a funo kthread_run() para criar e executar uma thread do kernel. Neste caso a thread criada e colocada no estado TASK_RUNNING, pronta para execuo. Para parar uma thread do kernel, voc pode usar a funo kthread_stop().

Embedded Labworks

KERNEL THREADS API


#include<linux/kthread.h> /*createakernelthread*/ structtask_struct*kthread_create( int(*threadfn)(void*data), void*data, constcharnamefmt[],...); /*createandrunakernelthread*/ structtask_struct*kthread_run(int(*threadfn)(void*data), void*data, constcharnamefmt[],...); /*stopakernelthread*/ intkthread_stop(structtask_struct*k);

Embedded Labworks

KERNEL THREADS API (EXEMPLO)


#include<linux/kthread.h> structtask_struct*mytask; /*kernelthread*/ staticintmythread(void*unused) { while(!kthread_should_stop()){ /*dosomething*/ } return(0); } /*createandstarttask*/ mytask=kthread_run(mythread,NULL,"%s","mythread"); /*stoptask*/ kthread_stop(mytask);

Embedded Labworks

EVENTOS

Boa parte das tarefas que desenvolvemos so baseadas em eventos, ou seja, elas devem esperar um evento para continuar seu processamento. Exemplos:

Eventos temporais ou delay (ex: esperar 100ms). Operao de I/O (ex: esperar a leitura de um arquivo). Evento de hardware (ex: esperar uma tecla ser pressionada). Mecanismos de sincronizao (ex: esperar a liberao de um mutex).

Uma forma de implementar tarefas que esperam eventos atravs de polling. Mas ao usar polling, a tarefa ir monopolizar e disperdiar ciclos preciosos de CPU.

Embedded Labworks

SLEEPING

O ideal nestes casos trabalhar com interrupo. Ou seja, colocar a tarefa para dormir e pedir para o kernel acord-la assim que o evento acontecer. Quando uma tarefa dorme, o kernel coloca ela em um dos estados TASK_INTERRUPTIBLE ou TASK_UNINTERRUPTIBLE, e seleciona outra tarefa para execuo. Assim que o evento acontecer, o kernel coloca a tarefa novamente no estado TASK_RUNNING, pronta para ser executada.

Embedded Labworks

SLEEPING (cont.)
Processo de comunicao via RS232
L um byte da porta serial

Outros processos so escalonados

Processo de comunicao via RS232


Retorna byte lido

Chamada de sistema ...


Dorme Requisita um byte da interface RS232 Acorda o processo

chamada de sistema

ISR porta serial

Embedded Labworks

WAIT QUEUES

Para dormir, voc deve declarar uma wait queue, que mantm uma lista de tarefas esperando um evento acontecer. Uma

queue definida atravs da varivel wait_queue_head_t, que pode ser inicializada de suas formas:
Estaticamente com a macro DECLARE_WAIT_QUEUE_HEAD(). Dinamicamente com a funo init_waitqueue_head().

wait

Os processos devem dormir usando uma das funes do tipo wait_event*() e devem ser acordados atravs das funes do tipo wake_up*().

Embedded Labworks

SLEEPING API
#include<linux/wait.h> #include<linux/sched.h> /*declarewaitqueuestatically*/ DECLARE_WAIT_QUEUE_HEAD(my_queue); /*declarewaitqueuedynamically*/ wait_queue_head_tmy_queue; init_waitqueue_head(&my_queue);

Embedded Labworks

SLEEPING API (cont.)


#include<linux/wait.h> /*SleepsuntilthetaskiswokenupandthegivenCexpression istrue.Caution:can'tbeinterrupted(can'tkilltheuser spaceprocess!)*/ ret=wait_event(queue,condition); /*Canbeinterrupted,butonlybyafatalsignal(SIGKILL). ReturnsERESTARSYSifinterrupted.*/ ret=wait_event_killable(queue,condition); /*Canbeinterruptedbyanysignal.ReturnsERESTARTSYSif interrupted.*/ ret=wait_event_interruptible(queue,condition);

Embedded Labworks

SLEEPING API (cont.)


#include<linux/wait.h> /*Alsostopssleepingwhenthetaskiswokenupandthe timeoutexpired.Returns0ifthetimeoutelapsed,nonzero iftheconditionwasmet.*/ ret=wait_event_timeout(queue,condition,timeout); /*Sameasabove,interruptible.Returns0ifthetimeout elapsed,ERESTARTSYSifinterrupted,positivevalueifthe conditionwasmet.*/ ret=wait_event_interruptible_timeout(queue,condition, timeout);

Embedded Labworks

SLEEPING API (cont.)


#include<linux/wait.h> /*wakesupallprocessesinthewaitqueue*/ wake_up(&queue); /*wakesupallprocesseswaitinginaninterruptiblesleepon thegivenqueue*/ wake_up_interruptible(&queue);

Embedded Labworks

SLEEPING API (EXEMPLO)


#include<linux/wait.h> #include<linux/sched.h> /*declarewaitqueue*/ wait_queue_head_twait; /*initializewaitqueue*/ init_waitqueue_head(&wait); /*waitonawaitqueue(calledonatask)*/ ret=wait_event_interruptible(wait,ready!=0); /*wakeupallprocesswaitingonthewaitqueue (calledinsideaninterrupthandler)*/ wake_up_interruptible(&wait);

Embedded Labworks

IMPLEMENTAO WAIT_EVENT()
#define__wait_event(wq,condition) do{ DEFINE_WAIT(__wait); for(;;){ prepare_to_wait(&wq,&__wait, TASK_UNINTERRUPTIBLE); if(condition) break; schedule(); } finish_wait(&wq,&__wait); }while(0);

Embedded Labworks

EXCLUSIVE E NON-EXCLUSIVE

Todas as funes que vimos at aqui (wait_event*()) colocam a tarefa em uma espera no exclusiva. Isso significa que todas as tarefas esperando no queue podem ser acordadas com as funes wake_up() e wake_up_interruptible(). Mas voc pode esperar um evento de forma exclusiva com a funo wait_event_interruptible_exclusive() .

Embedded Labworks

EXCLUSIVE E NON-EXCLUSIVE (cont.)

Desta forma, se voc tiver vrias tarefas esperando de forma esclusiva, as funes wake_up() e wake_up_interruptible() acordaro apenas uma delas. Portanto, o modo exclusivo faz sentido quando apenas uma tarefa ir "consumir" o evento. Mas se mesmo assim voc quiser acordar todas as tarefas que esto esperando de forma exclusiva, basta usar as funes wake_up_all() e wake_up_interruptible_all().

Embedded Labworks

LABORATRIO

Kernel threads e wait queues

Embedded Labworks

Linux Device Drivers

Gerenciamento de interrupes

Embedded Labworks

INTERRUPO

Um dos principais trabalhos do sistema operacional gerenciar e se comunicar com os dispositivos de hardware conectados ao sistema. Para isso, o kernel pode checar o status do hardware periodicamente (polling) e atuar de acordo. Mas como os dispositivo de hardware so muito mais lentos que a CPU, este mecanimos de acesso ao hardware disperdia preciosos ciclos de CPU, e no normalmente usado. Por este motivo, para conversar com o hardware, a CPU, e consequentemente o kernel, usam o mecanismo de interrupo.

Embedded Labworks

INTERRUPO (cont.)

O mecanismo de interrupo possibilita ao hardware a capacidade de sinalizar a CPU quando acontecer algum evento (o boto do mouse foi pressionado, uma tecla foi digitada, um pacote foi recebido da interface Ethernet, etc). Diferentes dispositivos esto associados diferentes nmeros de interrupo, tambm chamadas de linhas de IRQ (Interrupt Request). Quando a CPU recebe o sinal de interrupo, interrompe a execuo atual, e de acordo com a linha de IRQ, executa uma rotina de tratamento de interrupo registrada no sistema (tambm chamada de interrupt handler ou ISR - Interrupt Service Routine).

Embedded Labworks

INTERRUPO NO KERNEL

Uma rotina de tratamento de interrupo nada mais do que uma funo definida pelo device driver (com um prottipo bem definido que veremos mais adiante). Para um device driver usar uma linha de interrupo, ele deve:

Requisitar o uso da linha de IRQ quando for iniciar o uso do dispositivo de hardware atravs da funo request_irq(). Implementar a ISR (veremos as regras de implementao da ISR mais adiante). Liberar o uso da linha de IRQ quando no for mais usar o dispositivo atravs da funo free_irq().

Embedded Labworks

IRQ API
#include<linux/interrupt.h> /*requestIRQ,returns0onsuccess*/ intrequest_irq(unsignedintirq, irq_handler_thandler, unsignedlongirq_flags, constchar*devname, void*dev_id); /*freeIRQ*/ voidfree_irq(unsignedintirq,void*dev_id);

Embedded Labworks

IRQ FLAGS

Estas so as principais flags que podem ser passadas para registrar uma IRQ:

IRQF_SHARED: O canal de IRQ pode ser compartilhado por vrias

ISRs. Neste caso, o hardware precisa prover um mecanismo para que a ISR possa verificar a origem da interrupo.

IRQF_SAMPLE_RANDOM: usar a interrupo para alimentar a rotina

de gerao de nmeros aleatrios do kernel.

Embedded Labworks

IRQ API (EXEMPLO)


#include<linux/interrupt.h> /*requestIRQ*/ if(request_irq(irqn,mydriver_isr,IRQF_SHARED, "mydriver",mydriver.dev)){ pr_err("ErrorrequestingIRQ!\n"); [...] } /*freeIRQ*/ free_irq(irqn,mydriver.dev);

Embedded Labworks

IRQS REGISTRADAS NO SISTEMA

Para verificar as IRqs registradas no sistema, basta listar o arquivo /proc/interrupts:


#cat/proc/interrupts CPU0 1:45tzicmmc0 3:0tzicmmc1 6:0tzicsdma 31:44tzicIMXuart 39:5638tzici.MXTimerTick 62:0tzicimxi2c 87:6515tzicimx25fec 235:0gpiomxcmmc1 237:0gpiomxcmmc0 [...]

Embedded Labworks

PROTTIPO DE UMA ISR


/** *ISRprototype. *@irq:theIRQnumber *@dev_id:theopaquepointerpassedatrequest_irq() * *ShouldreturnIRQ_HANDLEDifinterruptwasrecognized *andhandledorIRQ_NONEifinterruptwasn'trecognized *ormanagedbymodule. */ irqreturn_tfoo_interrupt(intirq,void*dev_id);

Embedded Labworks

RESTRIES DE UMA ISR

Como no existe uma garantia de qual espao de endereamento o sistema estar quando uma interrupo acontecer, no possvel trocar dados com user space. A execuo da rotina de tratamento de interrupo gerenciada pela CPU, e no pelo escalonador. Ou seja, uma ISR roda em contexto de interrupo, e no em contexto de processo! Por este motivo, uma ISR no pode dormir, j que ela no roda em contexto de processo, e no pode ser escalonada. Em especfico, alocaes de memria devem ser feitas passando a flag GFP_ATOMIC.

Embedded Labworks

RESTRIES DE UMA ISR (cont.)

Desde o kernel 2.6.36, quando uma ISR executada:

A linha de IRQ da interrupo desabilitada em todas as CPUs (por isso uma ISR no precisa ser reentrante). Todas as interrupes locais (da mesma CPU) so desabilitadas. Por este motivo, uma ISR deve executar seu trabalho rapidamente e retornar.

Embedded Labworks

IMPLEMENTANDO UMA ISR

Reconhecer a interrupo, por exemplo setando um bit em um registrador do controlador de interrupes. Ler ou escrever dados do dispositivo. Sinalizar o processo esperando um evento do dispositivo, normalmente usando wait queues:
wake_up_interruptible(&mydriver_queue);

Embedded Labworks

ISR EXEMPLO 1
irqreturn_tmydriver_interrupt(intirq,void*dev_id) { /*ackinterrupt*/ ack(); /*readdevicedata*/ dev_id>data=getdata(); /*wakeupprocesswaitingonwaitqueue*/ wake_up(&mydriver_queue); /*returnIRQhandled*/ returnIRQ_HANDLED; }

Embedded Labworks

TOP HALF E BOTTOM HALF

Em alguns casos, uma interrupo precisa realizar uma quantidade grande de trabalho, ao mesmo tempo em que sua execuo precisa ser bem rpida. So necessidades conflitantes! Para estes casos, o processamento pode ser dividido em duas partes:

Top Half. Bottom Half.

Embedded Labworks

TOP HALF

Este prprio cdigo da ISR, que executado assim que acontece a interrupo. Deve retornar o mais rpido possvel, j que as interrupes esto desabilitadas. Por esse motivo, ele deve delegar o tratamento dos dados ou evento recebido para um mecanismo de bottom half.

Embedded Labworks

BOTTOM HALF

Ser executado em algum momento no futuro, com todas as interrupes habilitadas. Desta forma, o impacto menor se levar mais tempo para realizar o processamento delegado pela interrupo. No Linux, pode ser implementado atravs de trs mecanismos diferentes: softirqs, tasklets ou workqueues.

Embedded Labworks

ISR EXAMPLE 2
/*driverISR*/ irqreturn_tmydriver_interrupt(intirq,void*dev_id) { ack(); dev_id>data=getdata(); enable_bottom_half(); returnIRQ_HANDLED; } /*bottomhalfexecutewithinterruptsenabled*/ voidbottom_half_function() { /*dotheheavyworkhere!*/ [...] wake_up(&mydriver_queue); }

Embedded Labworks

SOFTIRQS

Softirqs so uma forma de processamento em bottom half. So executadas depois que todas as interrupes forem processadas, normalmente no fim do tratamento das interrupes. Rodam portanto em contexto de interrupo, por isso no podem bloquear (dormir). Mas como so executadas com todas as interrupes habilitadas, o impacto no tempo de resposta do sistema bem menor. Dependendo da carga do sistema, podem ser tratadas tambm em threads do kernel (vide ksoftirqd/X, onde X o nmero da CPU).

Embedded Labworks

SOFTIRQS (cont.)

A mesma softirq pode rodar em mltiplas CPUs ao mesmo tempo, por esse motivo preciso tomar cuidado extra com sua implementao (em especfico, ela precisa ser thread-safe). A quantidade de softirqs disponveis no sistema limitada (por esse motivo ela no usada normalmente por drivers, e sim por sub-sistemas do kernel, como o sub-sistema de rede). A lista de softirqs esta definida em <linux/interrupt.h>: HI, TIMER, NET_TX, NET_RX, BLOCK, BLOCK_IOPOLL, TASKLET, SCHED, HRTIMER, RCU. No muito comum o uso direto de softirqs por drivers. O mais comum usar as softirqs atravs da implementao de tasklets.

Embedded Labworks

TASKLETS

As tasklets so executadas nas softirqs HI e TASKLET. Como um tipo de softirq, tem as mesmas caractersticas desta, rodando em contexto de interrupo com todas as interrupes habilitadas. A nica diferena com relao s softirqs que garantido que s uma instncia da tasklet estar rodando ao mesmo tempo, mesmo em sistemas com mltiplas CPUs (SMP).

Embedded Labworks

TASKLETS (cont.)

Uma tasklet pode ser declarada estaticamente com a macro DECLARE_TASKLET() ou criada dinamicamente com a funo tasklet_init(). Uma ISR pode delegar trabalho para uma tasklet usando uma das duas funes abaixo:

tasklet_schedule(): executa a tasklet na softirq TASKLET. tasklet_hi_schedule(): executa a tasklet na softirq HI, que

possui maior prioridade.

Embedded Labworks

TASKLET API
#include<linux/interrupt.h> /*inittasklet*/ voidtasklet_init(structtasklet_struct*t, void(*func)(unsignedlong), unsignedlongdata); /*killtasklet*/ voidtasklet_kill(structtasklet_struct*t); /*scheduletasklet*/ voidtasklet_schedule(structtasklet_struct*t) /*taskletfunction*/ staticvoidtasklet_func(unsignedlongdata);

Embedded Labworks

TASKLET API (EXEMPLO)


#include<linux/interrupt.h> /*Thetaskletfunction*/ staticvoidatmel_tasklet_func(unsignedlongdata){ structuart_port*port=(structuart_port*)data; [...] } /*Registeringthetasklet(ex:onaninitfunction)*/ tasklet_init(&atmel_port>tasklet,atmel_tasklet_func, (unsignedlong)port); /*Removingthetasklet(ex:onacleanupfunction)*/ tasklet_kill(&atmel_port>tasklet); /*Triggeringexecutionofthetasklet(ex:onanISR)*/ tasklet_schedule(&atmel_port>tasklet);

Embedded Labworks

WORK QUEUES

Work queue um mecanismo genrico de deferir trabalho, no s limitado ao tratamento de interrupes. A funo definida como work queue executada em uma thread do kernel (kworker) com todas as interrupes habilitadas. Como uma work queue roda em uma thread do kernel, ela executada em contexto de processo, e por este motivo pode bloquear (dormir). Portanto, se o processamento deferido pela CPU precisa bloquear (dormir), em vez de usar tasklets, voc dever usar work queues.

Embedded Labworks

WORK QUEUES (cont.)

Em sua forma mais comum, a work queue uma interface para deferir trabalho para uma thread genrica do kernel (events/n ou kworker/n:id, dependendo da verso do kernel, onde n o nmero da CPU e id o id da work queue). Um trabalho a ser delegado (work) pode ser criado com DECLARE_WORK() ou INIT_WORK(), e normalmente acionado com schedule_work().

Embedded Labworks

WORK QUEUE API


#include<linux/workqueue.h> /*createworkstatically*/ DECLARE_WORK(name,void(*func)(void*)); /*createworkdinamically*/ INIT_WORK(structwork_struct*work,void(*func)(void*)); /*scheduleworkondefaultworkqueuethread*/ intschedule_work(structwork_struct*work); /*scheduledelayedworkondefaultworkqueuethread*/ intschedule_delayed_work(structwork_struct*work, unsignedlongdelay);

Embedded Labworks

CRIANDO NOVAS WORK QUEUES

As funes schedule_work() e schedule_delayed_work() iro delegar o trabalho para uma thread de tratamento de work queues genrica do sistema. Isso significa que seu processamento poder competir com o trabalho delegado por outros drivers ou sub-sistemas do kernel. Se voc precisa de mais performance ou exclusividade no tratamento dos trabalhos, voc pode criar uma nova work queue com a funo alloc_workqueue().

Embedded Labworks

WORK QUEUE API (cont.)


#include<linux/workqueue.h> /*createnewworkqueue*/ structworkqueue_struct*alloc_workqueue(char*name, unsignedintflags,intmax_active); /*scheduleworkonuserdefinedworkqueue*/ intqueue_work(structworkqueue_struct*wq, structwork_struct*work); /*scheduledelayedworkonuserdefinedworkqueue*/ intqueue_delayed_work(structworkqueue_struct*wq, structdelayed_work*work, unsignedlongdelay);

Embedded Labworks

WORKQUEUE API (EXEMPLO)


voidinit_function() { INIT_WORK(&gpio>work,pcf857x_irq_demux_work); [...] } staticirqreturn_tisr_function(intirq,void*data) { [...] schedule_work(&gpio>work); returnIRQ_HANDLED; } staticvoidpcf857x_irq_demux_work(structwork_struct*work) { /*dotheworkhere!*/ }

Embedded Labworks

COMPARANDO
Bottom Half Softirq Tasklet Work queues Contexto Interrupo Interrupo Processo Serializado No Sim (na mesma tasklet) No

Embedded Labworks

QUAL BOTTOM HALF USAR?

Work queues envolvem um overhead maior pelo uso de threads do kernel e pelas trocas de contexto envolvidas. Voc dever usar work queues quando existir a possibilidade do trabalho ser escalonado, ou seja, se o trabalho precisar dormir. Por outro lado, tasklets envolvem menos overhead e devem ser o mtodo preferido se seu trabalho no precisar dormir e puder ser executado em contexto de interrupo. Se voc precisar de muita performance, avalie a possibilidade de usar softirqs no lugar de tasklets.

Embedded Labworks

OUTROS MECANISMOS

Existem ainda outros mecanismos que podem ser usados no tratamento de interrupes no Linux, dentre eles:

Threaded Interrupts. Kernel Timers.

Embedded Labworks

THREADED INTERRUPTS

O suporte threaded interrupts tem suas origens na rvore de realtime do kernel e foi adicionado ao Linux a partir da verso 2.6.30. Seu principal objetivo diminuir a latncia do sistema como um todo, fazendo com que uma interrupo seja tratada dentro de uma thread do kernel, rodando em contexto de processo. Por este motivo, permitido bloquear (dormir) em uma threaded interrupt. Para threaded request_threaded_irq(). registrar uma interrupt, basta usar a funo

Mais informaes em <linux/interrupt.h>.

Embedded Labworks

KERNEL TIMERS

Kernel timers um mecanismo usado para programar a execuo de um trabalho em determinado momento no futuro. Para us-lo, voc precisa declarar uma estrutura do tipo timer_list e inicializar esta estrutura basicamente com o valor do timer e a funo de callback. Os kernel timers rodam em uma softirq (TIMER_SOFTIRQ), ou seja, em contexto de interrupo, e portanto no podem bloquear! Mais informaes em <linux/timer.h>.

Embedded Labworks

LABORATRIO

Trabalhando com interrupes

Embedded Labworks

Linux Device Drivers

Mecanismos de sincronizao

Embedded Labworks

MULTITHREAD E CONCORRNCIA

Em um ambiente multithread, os recursos compartilhados do sistema precisam ser protegidos de acesso concorrente. Um trecho de cdigo que acessa recursos compartilhados chamado de regio crtica (critical session). Para previnir o acesso concorrente uma regio crtica, o acesso deve ser atmico, ou seja, a operao em cima do recurso compartilhado deve ser executada o comeo ao fim sem interrupo.

Embedded Labworks

CONCORRNCIA NO KERNEL

Em termos de concorrncia, o kernel tem as mesmas restries de um programa multithread: o seu estado global e visvel todos os contextos de execuo (interrupo e processos). E os problemas de concorrncia no kernel podem acontecer porque:

Uma interrupo pode interromper a execuo de um processo, e ambos podem estar usando recursos compartilhados. A preempo do kernel pode interromper a execuo de uma chamada de sistema para executar outra chamada de sistema, e ambas podem estar usando recursos compartilhados. Em sistemas SMP (com mltiplas CPUs) podemos ter processos rodando realmente em paralelo em diferentes processadores, que podem estar usando recursos compartilhados.

Embedded Labworks

SOLUO

Sempre que possvel, mantenha um estado local dos processos. Caso voc precise de recursos compartilhados, identifique quais so estes recursos:

Se o seu recurso compartilhado for um nmero inteiro, voc pode usar as funes atmicas disponibilizadas pelo kernel. Em outros casos, como estruturas de dados mais complexas ou recursos de hardware, voc dever usar um mecanismo de locking.

Embedded Labworks

ATOMIC OPERATIONS

O recurso de operaes atmicas do kernel til quando o recurso compartilhado esta armazenado em uma varivel do tipo inteiro. Mesmo uma operao simples em inteiros do tipo x++ ou x|=1 no garantida que seja atmica em todas as arquiteturas! O uso das funes de operaes atmicas garantem acesso atmico s variveis do tipo inteiro. O Linux fornece duas APIs de operaes atmicas:

Operaes em nmero inteiros, definida em <asm/atomic.h>. Operaes em bits, definida em <asm/bitops.h>.

Embedded Labworks

ATOMIC OPERATIONS API


#include<asm/atomic.h> /*setorreadthethevariable*/ voidatomic_set(atomic_t*v,inti); intatomic_read(atomic_t*v); /*changevariable*/ voidatomic_inc(atomic_t*v); voidatomic_dec(atomic_t*v); voidatomic_add(inti,atomic_t*v); voidatomic_sub(inti,atomic_t*v);

Embedded Labworks

ATOMIC OPERATIONS API (cont.)


#include<asm/atomic.h> /*changeandreturntrueifresultiszero*/ intatomic_inc_and_test(atomic_t*v); intatomic_dec_and_test(atomic_t*v); intatomic_add_and_test(inti,atomic_t*v); intatomic_sub_and_test(inti,atomic_t*v); /*changeandreturnresult*/ intatomic_inc_and_return(atomic_t*v); intatomic_dec_and_return(atomic_t*v); intatomic_add_and_return(inti,atomic_t*v); intatomic_sub_and_return(inti,atomic_t*v);

Embedded Labworks

ATOMIC BIT OPERATIONS API


#include<asm/bitops.h> /*set,clearortoggleagivenbit*/ voidset_bit(intnr,unsignedlong*addr); voidclear_bit(intnr,unsignedlong*addr); voidchange_bit(intnr,unsignedlong*addr); /*testbitandreturnitsvalue*/ inttest_bit(intnr,unsignedlong*addr); /*setabitandreturnitsoldvalue*/ inttest_and_set_bit(intnr,unsignedlong*addr); inttest_and_clear_bit(intnr,unsignedlong*addr); inttest_and_change_bit(intnr,unsignedlong*addr);

Embedded Labworks

LOCKING

Nem sempre um recurso compartilhado pode ser representado por uma varivel do tipo inteiro. Na maioria das vezes, a regio crtica que precisamos proteger atua em cima de estruturas de dados mais complexas. Para estes casos, devemos proteger o acesso ao recurso compartilhado atravs de mecanismos de locking.

Embedded Labworks

LOCKING (cont.)
Processo 1 Processo 2

lock()

wait_lock()

Seo crtica

unlock()

Embedded Labworks

SEMFORO

O semforo um mecanismo de comunicao entre tarefas. Quando uma tarefa tenta adquirir um semforo que esta indisponvel, a tarefa colocada em uma fila de espera e bloqueia (dorme). Quando o semforo ficar disponvel, a tarefa acordada, adquire o semforo e pode realizar seu trabalho. Aps realizar o processamento, a tarefa dever liberar o semforo para outra tarefa utiliz-lo se necessrio.

Embedded Labworks

SEMFORO (cont.)

Um semforo pode ser usado como mecanismo de comunicao de eventos entre tarefas (sincronizao) ou como um mecanismo de locking. Durante bastante tempo, o Linux implementava o mecanismo de locking atravs de semforos. Mais informaes sobre a API de semforos em <asm/semaphore.h>. A partir da verso 2.6.16, a funcionalidade de mutex (abreviao de mutual exclusion) foi implementada e adotada como mecanismo padro de locking para gerenciar o acesso recursos compartilhados no kernel.

Embedded Labworks

MUTEX

Antes de entrar em uma regio crtica, um lock deve ser adquirido. Se o lock no estiver disponvel, a tarefa dever bloquear (dormir) aguardando o lock ficar disponvel. Assim que o lock ficar disponvel, a tarefa acordada, adquire o lock e realiza seu processamento. Aps o processamento, a tarefa deve liberar o lock. Como o processo que requisita o lock pode bloquear (dormir) se este lock estiver indisponvel, um mutex s pode ser usado em contexto de processo.

Embedded Labworks

MUTEX API
#include<linux/mutex.h> /*initializingamutexstatically*/ DEFINE_MUTEX(mutex_name); /*initializingamutexdynamically*/ voidmutex_init(structmutex*lock);

Embedded Labworks

MUTEX API (cont.)


#include<linux/mutex.h> /*triestolockthemutex,sleepsotherwise.Can'tbe interrupted,resultinginprocessesyoucannotkill!*/ voidmutex_lock(structmutex*lock); /*same,butcanbeinterruptedbyafatal(SIGKILL)signal. Ifinterrupted,returnsanonzerovalueanddoesn'thold thelock.Testthereturnvalue!*/ intmutex_lock_killable(structmutex*lock); /*same,butcanbeinterruptedbyanysignal*/ intmutex_lock_interruptible(structmutex*lock);

Embedded Labworks

MUTEX API (cont.)


#include<linux/mutex.h> /*neverwaits,returnsanonzerovalueifthemutexis notavailable*/ intmutex_trylock(structmutex*lock); /*justtellswhetherthemutexislockedornot*/ intmutex_is_locked(structmutex*lock); /*releasesthelock,doitassoonasyouleavethe criticalsection*/ voidmutex_unlock(structmutex*lock);

Embedded Labworks

MUTEX API (EXEMPLO)


#include<linux/mutex.h> staticint__devinitads7846_probe(structspi_device*spi) { [...] mutex_init(&ts>lock); [...] } staticvoidads7846_enable(structads7846*ts) { mutex_lock(&ts>lock); if(ts>disabled){ ts>disabled=false; [...] } mutex_unlock(&ts>lock); }

Embedded Labworks

DEFICINCIAS DOS MUTEXES

Os mutexes devem ser o mecanismo preferido para proteger o acesso concorrente regies crticas, mas eles possuem duas principais deficincias:

Existe um overhead de pelo menos duas trocas de contexto na sua execuo (colocar a tarefa para dormir quando o mutex estiver sendo usado e acordar a tarefa quando o mutex for liberado). O fato de bloquear no permite seu uso em interrupes. Por exemplo, um sistema multicore onde temos uma CPU executando uma ISR e outra CPU executando um processo, ambos acessando um recurso compartilhado.

para estes casos especficos que existem os spinlocks.

Embedded Labworks

SPINLOCKS (cont.)

O spinlock um mecanismo de locking criado para ser usado em trechos de cdigo que no podem (ou no querem) bloquear, como por exemplo ISRs ou bottom halves, e so importantes em sistemas multicore onde temos processos e interrupes acessando um recurso compartilhado ao mesmo tempo. Ele implementado como um loop que fica verificando (spinning) em um loop at que o lock esteja disponvel. Por este motivo, a seo crtica protegida pelo spinlock deve ser executada rapidamente e no pode bloquear (dormir).

Embedded Labworks

SPINLOCKS (cont.)

Alm de verificar o lock, um spinlock desabilita a preempo do kernel na CPU em que esta executando. O lock do spinlock s faz sentido em sistemas multicore. Por este motivo, em sistemas com um nico core o tratamento do lock removido, e s o cdigo que habilita/desabilita a preempo compilado. Em sistemas com um nico core e com a preempo do kernel desabilitada, o cdigo relacionado ao spinklock removido completamente!

Embedded Labworks

SPINLOCK API
#include<linux/spinlock.h> /*initializingaspinlockstatically*/ DEFINE_SPINLOCK(my_lock); /*initializingaspinlockdynamically*/ voidspin_lock_init(spinlock_t*lock);

Embedded Labworks

SPINLOCK API (cont.)


#include<linux/spinlock.h> /*doesn'tdisableinterrupts.Usedforlockinginprocess context(criticalsectionsinwhichyoudonotwantto sleep)*/ voidspin_lock(spinlock_t*lock); voidspin_unlock(spinlock_t*lock); /*disables/restoresIRQsonthelocalCPU.Typicallyused whenthelockcanbeaccessedinbothprocessand interruptcontext,topreventpreemptionbyinterrupts*/ voidspin_lock_irqsave(spinlock_t*lock, unsignedlongflags); voidspin_unlock_irqrestore(spinlock_t*lock, unsignedlongflags);

Embedded Labworks

SPINLOCK API (cont.)


#include<linux/spinlock.h> /*disablessoftwareinterrupts,butnothardwareones. Usefultoprotectshareddataaccessedinprocesscontext andinasoftinterrupt(bottomhalf).Noneedto disablehardwareinterruptsinthiscase*/ voidspin_lock_bh(spinlock_t*lock); voidspin_unlock_bh(spinlock_t*lock);

Embedded Labworks

SPINLOCK API (EXEMPLO)


#include<linux/spinlock.h> staticvoidserio_init_port(structserio*serio) { [...] spin_lock_init(&serio>lock); [...] } irqreturn_tserio_interrupt(structserio*serio, unsignedchardata,unsignedintdfl) { unsignedlongflags; spin_lock_irqsave(&serio>lock,flags); [...] spin_unlock_irqrestore(&serio>lock,flags); }

Embedded Labworks

DESABILITANDO PREEMPO

Em sistemas com apenas uma CPU, onde o recurso compartilhado acessado apenas por processos, no precisamos de um spinlock, j que este recurso no ser acessado por outra CPU. Neste caso, temos duas opes: usar um mutex ou desabilitar a preempo do kernel. Se voc quiser evitar o overhead do uso de um mutex, pode usar a famlia de funes para habilitar e desabilitar a preempo, como as funes preempt_enable() e preempt_disable(). Estas funes esto definidas em <linux/preempt.h>.

Embedded Labworks

DESABILITANDO INTERRUPES

Em sistemas com uma nica CPU, ao compartilhar dados entre um processo e uma interrupo, voc pode simplesmente desabilitar as interrupes. Para isso, voc pode usar a famlia de funes de habilitao e desabilitao de interrupes, como por exemplo as funes local_irq_disable(), local_irq_enable(), local_irq_save() e local_irq_restore(). Mais informaes sobre estas funes em <linux/irqflags.h>. Lembre-se de que ao desabilitar as interrupes, voc estar aumentando o tempo de latncia do sistema.

Embedded Labworks

RESUMO

Compartilhando dados entre processos:

Como padro, use mutex. Caso a performance seja importante, avalie a possibilidade do uso de spinlocks, mas lembre-se de que spinlocks aumentam o tempo de latncia do sistema.

Compartilhando dados entre processos e interrupes:

Evite, se possvel! Use spinlocks.

Lembre-se: desenvolva o driver sempre levando em considerao que ele pode rodar em um sistema SMP e com a preempo do kernel habilitada!

Embedded Labworks

DEADLOCKS
SITUAO 1 SITUAO 2

lock(1)

lock(1)

lock(2)

lock(1)

lock(2)

lock(1)

deadlock

deadlock

Embedded Labworks

READER-WRITE SPINLOCKS

Quando um processo pretende escrever em um recurso compartilhado, importante que nenhum outro processo tenha acesso ao recurso compartilhado. Porm, quando um processo pretende ler de um recurso compartilhado, no existe problema se outro processo tambm tentar ler deste mesmo recurso compartilhado. Neste caso podemos ter um lock exclusivo para escritas e um lock compartilhado para leituras. O Linux implementa esta funcionalidade atravs de spinlocks do tipo Reader-Writer.

Embedded Labworks

READER-WRITE SPINLOCKS API


#include<linux/rwlock.h> /*initialize*/ DEFINE_LOCK(mydriver_rwlock); voidrwlock_init(rwlock_t*lock); /*lockonread*/ voidread_lock(rwlock_t*lock); voidread_unlock(rwlock_t*lock); /*lockonwrite*/ voidwrite_lock(rwlock_t*lock); voidwrite_unlock(rwlock_t*lock);

Embedded Labworks

OUTROS MECANISMOS

Sequencial locks: tambm chamado de seqlock, um mecanismo parecido com o Reader-Write lock, mas prioriza escritas ao invs de leituras. Mais informaes em <linux/seqlock.h>. RCU (Read Copy Update): um outro mecanismo parecido com o Reader-Write lock, com as rotinas de leitura muito mais rpidas, porm com a rotina de escrita mais lenta. Mais informaes em <linux/rcupdate.h>.

Embedded Labworks

COMPLETION VARIABLE

A varivel completion um exemplo de uso de semforos como mecanismo de comunicao de eventos (notificao) entre tarefas. Uma tarefa espera (dorme) na varivel completion enquanto outra tarefa realiza algum processamento. Quando a outra tarefa finalizar o processamento, ela utiliza a varivel completion para acordar qualquer outra tarefa que esteja esperando. Diferentemente de semforos ou wait queues, usando completion voc consegue acordar vrias tarefas ao mesmo tempo.

Embedded Labworks

COMPLETION VARIABLE API


#include<linux/completion.h> /*initializecompletionvariable*/ DECLARE_COMPLETION(x); voidinit_completion(structcompletion*x); /*waitforcompletionvariable*/ voidwait_for_completion(structcompletion*x); /*waitforcompletionvariablereturnonsignal*/ intwait_for_completion_interruptible(structcompletion*x); /*sinalizecompletionvariable*/ voidcomplete(structcompletion*x); /*sinalizecompletionvariableonallthreads*/ voidcomplete_all(structcompletion*x);

Embedded Labworks

LABORATRIO

Usando mecanismos de sincronizao

Embedded Labworks

Linux Device Drivers

Kernel Debugging

Embedded Labworks

KERNEL DEBUGGING

Debugging do kernel bem mais difcil quando comparado ao debugging de aplicaes. Um bug em uma aplicao derruba apenas a aplicao, j um bug no kernel pode derrubar todo o sistema! Debugging do kernel exige conhecimentos do sistema operacional e das diversas tcnicas de anlise, tracing e profiling que estudaremos nesta seo do treinamento.

Embedded Labworks

DEBUGGING COM MENSAGENS

A funo printk(), definida em <linux/printk.h>, a responsvel por imprimir mensagens no kernel, podendo ser chamada tanto em contexto de processo quanto em contexto de interrupo. Ela tem o mesmo prottipo da funo printf() usada em user space:
intprintk(constchar*s,...);

Todas as mensagens do kernel so armazenadas em um buffer circular (ring buffer), cujo tamanho pode ser definido em tempo de compilao na opo CONFIG_LOG_BUF_SHIFT ou em tempo de execuo no parmetro de boot log_buf_len. As mensagens so normalmente exibidas na console e podem ser emitidas a qualquer momento com a ferramenta dmesg.

Embedded Labworks

NVEIS DE LOG

Por padro, passa-se uma macro indicando o nvel (ou prioridade) da mensagem ao imprim-la:
printk(KERN_WARNING"warning:skippingphysicalpage0\n");

Todas as mensagens so armazenadas, mas apenas as mensagens com nvel de log menor ou igual ao configurado sero exibidas na console. O kernel define os seguintes nveis de mensagens de log:
0(KERN_EMERG)systemisunusable 1(KERN_ALERT)actionmustbetakenimmediately 2(KERN_CRIT)criticalconditions 3(KERN_ERR)errorconditions 4(KERN_WARNING)warningconditions 5(KERN_NOTICE)normalbutsignificantcondition 6(KERN_INFO)informational 7(KERN_DEBUG)debuglevelmessages

Embedded Labworks

NVEIS DE LOG (cont.)

O nvel de log padro pode ser definido em tempo de compilao na opo CONFIG_DEFAULT_MESSAGE_LOGLEVEL, no boot passando o parmetro loglevel ou em tempo de execuo no arquivo /proc/sys/kernel/printk. Outros parmetros de boot que podem alterar o comportamento das mensagens de log do kernel:

debug: Habilita o nvel 7 (KERN_DEBUG) de mensagens de log. ignore_loglevel: Habilita todos os nveis de mensagens (tambm pode ser habilitado em /sys/module/printk/parameters/ ignore_loglevel). quiet: Configura o nvel de log como 4 (KERN_WARNING).

Embedded Labworks

NOVAS FUNES

Hoje temos outras opes para exibir mensagens de log no kernel, e no mais comum o uso da funo printk() para debugging. Pode-se usar a famlia de funes pr_*(), definida em <linux/printk.h>:
pr_emerg(),pr_alert(),pr_crit(),pr_err(),pr_warning(), pr_notice(),pr_info(),pr_cont(),pr_debug().

Ou a famlia de funes dev_*(), definida em <linux/device.h>. Estas funes recebem como argumento um ponteiro para uma estrutura do tipo device (estudaremos esta estrutura mais adiante).
dev_emerg(),dev_alert(),dev_crit(),dev_err(), dev_warning(),dev_notice(),dev_info(),dev_dbg().

Embedded Labworks

MENSAGENS DE DEBUG

As funes pr_debug() e dev_dbg() s so compiladas se voc habilit-las no kernel. Quando o kernel compilado com a opo CONFIG_DEBUG, estas mensagens de debug so compiladas e exibidas com a funo printk() no nvel de debug do kernel. Quando o kernel compilado com a opo CONFIG_DYNAMIC_DEBUG, as mensagens de debug podem ser habilitadas por arquivo, por mdulo ou at por mensagem! Veja a documentao do kernel em Documentation/dynamicdebughowto.txt. Quando nenhuma destas duas opes do kernel estiverem habilitadas, as mensagens de debug impressas com pr_debug() e dev_dbg() no so compiladas.

Embedded Labworks

DEBUGFS

O debugfs um sistema de arquivos virtual que exporta informaes de debug para user space. Habilite a opo CONFIG_DEBUG_FS em Kernel hacking -> Debug Filesystem. Depois s montar o debugfs:
#mounttdebugfsnone/sys/kernel/debug #ls/sys/kernel/debug/ asocbluetoothhidmmc1 bdigpiommc0usb

A API esta documentada do DocBook do kernel: http://free-electrons.com/kerneldoc/latest/DocBook/filesystems/index.html

Embedded Labworks

DEBUGFS API
#include<linux/debugfs.h> /*createasubdirectoryforyourdriver*/ structdentry*debugfs_create_dir(constchar*name, structdentry*parent); /*exposeanintegerasafileindebugfs,whereuisfor decimalrepresentationandxforhexadecimal representation*/ structdentry*debugfs_create_{u,x}{8,16,32}( constchar*name,mode_tmode, structdentry*parent,u8*value); /*exposeabinaryblobasafileindebugfs*/ structdentry*debugfs_create_blob(constchar*name, mode_tmode,structdentry*parent, structdebugfs_blob_wrapper*blob);

Embedded Labworks

OUTROS MECANISMOS

Alguns mecanismos de debugging foram bastante usados, mas agora seu uso no mais comum:

Usar comandos especiais de ioctl() para debugging (use debugfs). Usar entradas especiais no sistema de arquivos proc (use debugfs). Usar entradas especiais no sysfs (use debugfs). Usar printk() (use a famlia de funes pr_*() ou dev_*()).

Embedded Labworks

MAGIC SYSRQ KEY

A Magic Sysrq Key uma combinao de teclas manipulada pelo kernel, que pode ser usada como ferramenta de anlise, debug e recuperao do kernel:

No PC: [Alt] + [SysRq] + <caractere>. Em alguns teclados, a tecla SysRq a tecla Print Screen. Pela console serial: <break> + <caractere>. Em todas plataformas: escrever o caractere diretamente no arquivo /proc/sysrqtrigger.

Deve

habilitada no kernel atravs da opo CONFIG_MAGIC_SYSRQ, e pode ser habilitada ou desabilitada em tempo de execuo atravs do arquivo /proc/sys/kernel/sysrq.

ser

Embedded Labworks

MAGIC SYSRQ KEY (cont.)

Exemplos de comandos:

b: Reinicia o sistema automaticamente. e: Envia o sinal SIGTERM para todos os processos (menos o init). t: Mostra o stack (dump) de todos os processos em execuo. w: Mostra o stack (dump) dos processos bloqueados (dormindo). c: Fora um crash do kernel desreferenciando um ponteiro nulo.

Mais informaes e comandos disponveis na documentao do kernel em Documentation/sysrq.txt.

Embedded Labworks

KERNEL OOPS

Um oops um mecanismo de comunicao do kernel para notificar o usurio que um erro aconteceu. Este erro pode acontecer por diversos motivos, como por exemplo acesso ilegal regies de memria ou execuo de instrues invlidas. Quando acontece um oops o kernel emite uma mensagem na console, exibindo o status atual do sistema no momento em que aconteceu o problema, incluindo um dump dos registradores e o back trace do stack. Aps o oops, o kernel ir tentar se recuperar e resumir a execuo, mas dependendo do erro, nem sempre isso possvel. Portanto, o kernel pode travar aps o oops (kernel panic!).

Embedded Labworks

KERNEL OOPS (EXEMPLO)


Internalerror:Oops:817[#1]PREEMPT pc:[<80231bb8>]lr:[<80231fe8>]psr:60000093 sp:df4f5f18ip:df4f5e88fp:00000002 r10:00000000r9:00000000r8:00000007 r7:60000013r6:808214c8r5:00000000r4:00000063 r3:00000001r2:00000000r1:00000000r0:00000063 Stack:(0xdf4f5f18to0xdf4f6000) 5f00:00000002802320b4 5f20:df4d940000000002000a8d4800000000df4f5f80802320e4df1f7e00800fea88 5f40:00000002df4d9400000a8d48df4f5f8000000000000000007ec2271c800bfff0 [<80231bb8>](sysrq_handle_crash+0x14/0x20)from[<80231fe8>] (__handle_sysrq+0xdc/0x1a8) [<80231fe8>](__handle_sysrq+0xdc/0x1a8)from[<802320e4>] (write_sysrq_trigger+0x30/0x38) [<802320e4>](write_sysrq_trigger+0x30/0x38)from[<800fea88>] (proc_reg_write+0xb4/0xc8) [<800fea88>](proc_reg_write+0xb4/0xc8)from[<800bfff0>] (vfs_write+0xac/0x154) [<800bfff0>](vfs_write+0xac/0x154)from[<800c0144>](sys_write+0x3c/0x68) [<800c0144>](sys_write+0x3c/0x68)from[<80030f80>] (ret_fast_syscall+0x0/0x30) Kernelpanicnotsyncing:Fatalexception

Embedded Labworks

CONFIGURANDO O OOPS

No exemplo anterior, os endereos estavam convertidos para os nomes das respectivas funes, facilitando o processo de debugging. Para que esta funcionalidade esteja disponvel, habilite a opo CONFIG_KALLSYMS na configurao do kernel (antes o kernel s exibia os endereos e era necessrio usar um programa chamado ksymoops para fazer a anlise).

Embedded Labworks

NOTIFICANDO PROBLEMAS

Se necessrio, possvel gerar um kernel oops atravs das macros BUG() ou BUG_ON(). Exemplos:
if(erro)BUG(); BUG_ON(erro);

Se quiser gerar direto um kernel panic, use a funo panic():


if(erro_geral) panic("Algumacoisaterrvelaconteceu!");

J se o que voc quer s imprimir o back trace do stack, use a funo dump_stack().

Embedded Labworks

OUTRAS OPES DE DEBUG

O Linux possui diversas funcionalidades de debugging integradas ao kernel. Para us-las basta habilitar a opo CONFIG_DEBUG_KERNEL no menu de configurao, e depois habilitar a funcionalidade de debug desejada no menu "Kernel hacking". Alguns exemplos:

CONFIG_DEBUG_MUTEXES: habilita checagem do uso de mutexes. CONFIG_DEBUG_SPINLOCK: habilita checagem de spinlocks. CONFIG_SLUB_DEBUG_ON: checagem na camada de alocao de memria. CONFIG_DEBUG_KMEMLEAK: habilita checagem de memory leak. CONFIG_DEBUG_LL (arm e unicore32): habilita o debugging de baixo nvel, til

se o kernel no inicia ou trava no boot.

Embedded Labworks

GDB

O GDB (GNU Debugger) o debugger padro do projeto GNU, disponvel para diversas arquiteturas. http://www.gnu.org/software/gdb/ Interface via console, mas com diversos frontends disponveis (Eclipse, DDD, GDB/Insight, etc). Permite iniciar o processo de debugging de um kernel em execuo:
$gdbvmlinux/proc/kcore

O acesso somente de leitura. Voc consegue listar o contedo de uma funo, exibir o valor de variveis, etc; mas no consegue fazer alteraes ou colocar breakpoints por exemplo.

Embedded Labworks

KDB

O kdb um debugger que permite examinar a memria e as estruturas de dados do kernel enquanto o sistema esta em execuo. Durante um bom tempo era disponibilizado atravs de um conjunto de patches, mas foi integrado ao mainline na verso 2.6.35. Fornece uma interface de linha de comandos, permitindo realizar operaes tpicas de um debugger como step, stop, run, colocar breakpoints, disassembly de instrues, etc. A tecla Pause pode ser usada para entrar no modo de debug. No trabalha no nvel do cdigo-fonte, apenas no nvel de instrues assembly!

Embedded Labworks

KGDB

O KGDB permite que a execuo do kernel seja controlada remotamente via conexo serial (ou rede) atravs do gdb rodando em uma outra mquina. Esta funcionalidade foi includa no kernel deste a verso 2.6.26 (x86 e sparc) e 2.6.27 (arm, mips e ppc). possvel controlar quase tudo, ler e escrever em variveis, executar passo-a-passo, e at colocar breakpoints em rotinas de tratamento de interrupo!

Embedded Labworks

CONFIGURANDO O KERNEL

Para usar o KGDB, voc precisa habilitar as seguintes opes:

CONFIG_KGDB: Habilita o KGDB. CONFIG_KGDB_SERIAL_CONSOLE: Habilita o driver de I/O para a

comunicao entre o host e o target via console serial.

Alm destas configuraes, algumas outras opes podem ajudar se forem habilitadas:

CONFIG_DEBUG_INFO: Para compilar o kernel com smbolos de

debugging.

CONFIG_FRAME_POINTER: Ajuda a produzir back traces de stack mais

confiveis.

CONFIG_KGDB_KDB: Frontend para o KGDB.

Embedded Labworks

USANDO O KGDB

Para habilitar o kgdb, basta passar os seguintes parmetros de boot para o kernel:
kgdboc=<ttydevice>,[baud]kgdbwait

Exemplo:
kgdboc=ttyS0,115200kgdbwait

Onde:

kgdboc: indica a conexo fsica com o KGDB. kgdbwait: faz o kgdb esperar por uma conexo de debug.

Embedded Labworks

USANDO O KGDB (cont.)

Na sua mquina de desenvolvimento, inicie o gdb conforme abaixo:


$gdb./vmlinux (gdb)setremotebaud115200 (gdb)targetremote/dev/ttyS0

Lembre-se de usar o gdb do seu (cross-compiling) toolchain. No target, interrompa o kernel com [Alt] + [SyrRq] + [g]. Assim que estabelecer a conexo, voc poder debugar o kernel como se ele fosse uma aplicao!

Embedded Labworks

USANDO O KGDB (cont.)

Voc tambm pode debugar via KGDB pela rede (UDP/IP), passando o parmetro de boot kgdboe para o kernel (precisa aplicar um patch, j que esta funcionalidade ainda no esta no mainline). Mais informaes sobre o KGDB em: http://free-electrons.com/kerneldoc/latest/DocBook/kgdb/

Embedded Labworks

DEBUGGING COM JTAG

JTAG uma interface fsica que permite acesso modulos de debug integrados ao core da CPU, onde podemos coloc-la em halt, inspecionar registradores, memria, colocar breakpoints, etc. uma interface de debugging necessria quando trabalhamos no porte do bootloader ou do kernel para determinada plataforma, trabalho tambm chamado de board bring-up.

Embedded Labworks

DEBUGGING COM JTAG (cont.)

Precisamos basicamente de 4 componentes para trabalhar com debugging via JTAG:

Hardware (kit de desenvolvimento) com suporte JTAG. Adaptador JTAG (Ex: Flyswatter). Debugger (Ex: GDB). Software de interface entre o adaptador JTAG e o debugger (Ex: OpenOCD).

Mais informaes em: http://sergioprado.org/linux-kernel-debugging-com-jtag/

Embedded Labworks

ANLISE COM KEXEC/KDUMP

Kexec uma chamada de sistema que torna possvel executar um novo kernel sem reiniciar ou passar pelo bootloader/BIOS. til no processo de debugging quando utilizado em conjunto com o kdump, um mecanismo para analisar um dump do kernel. Para mais detalhes consulte a documentao do kdump em
Documentation/kdump/kdump.txt .

Embedded Labworks

QUANDO NADA RESOLVER

Se voc esgotar todas as possibilidades de debugging, e mesmo assim no encontrar o problema... no se desespere! Voc ainda tem a comunidade! Se o problema esta em uma verso do kernel fornecida pelo fabricante do chip, estes fabricantes mantm um frum com engenheiros especializados para te ajudar. Se o problema esta na verso atual do kernel, voc pode mandar um e-mail para a lista de discusso do kernel (LKML) descrevendo o problema e as suas descobertas.

Embedded Labworks

QUANDO NADA RESOLVER (cont.)

Se o problema esta em um driver que voc fez, existem listas de discusso ou canais de IRC que podem te ajudar, como o Kernel Newbies. http://kernelnewbies.org/ E lembre-se, voc sempre ter o e-mail do Sergio Prado!

Embedded Labworks

Linux Device Drivers

Tracing

Embedded Labworks

KERNEL PROBES

Kernel probes fornecem um mecanismo de instrumentao no kernel. Com esta funcionalidade possvel extrair informaes do kernel e alterar seu comportamento (aplicar patches) em tempo de execuo, sem precisar reiniciar o sistema! A implementao genrica de kernel probes chamada de kprobes, e pode ser usada para instrumentar qualquer instruo do kernel. Os kprobes possuem ainda duas variantes:

jprobes: usados para instrumentar chamadas de funo. returnprobes: usados para instrumentar retornos de funo.

Embedded Labworks

KERNEL PROBES (cont.)

O principio bsico do kprobe baseado no uso de interrupes de software. Para usar a infraestrutura de kprobes, voc precisa desenvolver um mdulo do kernel e gerenciar os pontos de instrumentao atravs de uma estrutura do tipo kprobe. A limitao do uso de kprobes esta na dificuldade de associar o cdigo-fonte com o binrio gerado (otimizaes do compilador, recursos da linguagem C como macros e funes inline, etc). Mais informaes sobre kprobes em Documentation/kprobes.txt.

Embedded Labworks

SYSTEMTAP

O SystemTap uma infraestrutura de tracing que usa kprobes para facilitar o trabalho de instrumentao no kernel. http://sourceware.org/systemtap/ Assim como os kprobes, ele tambm elimina a necessidade de modificar e recompilar o kernel para investigar um problema funcional ou de performance. Utiliza uma linguagem simples de script (diversos exemplos disponveis na Internet). Tutorial e exemplos do SystemTap disponveis no ambiente de laboratrio do treinamento em /opt/labs/docs/guides/systemtap.pdf.

Embedded Labworks

SYSTEMTAP (EXEMPLO)
#straceopen.stp probesyscall.open { printf("%s(%d)open(%s)\n",execname(),pid(),argstr) } probetimer.ms(4000)#after4seconds { exit() }

Embedded Labworks

SYSTEMTAP (TESTANDO EXEMPLO)


#stapstraceopen.stp vmwareguestd(2206)open("/etc/redhatrelease",O_RDONLY) hald(2360)open("/dev/hdc",O_RDONLY|O_EXCL|O_NONBLOCK) hald(2360)open("/dev/hdc",O_RDONLY|O_EXCL|O_NONBLOCK) hald(2360)open("/dev/hdc",O_RDONLY|O_EXCL|O_NONBLOCK) df(3433)open("/etc/ld.so.cache",O_RDONLY) df(3433)open("/lib/tls/libc.so.6",O_RDONLY) df(3433)open("/etc/mtab",O_RDONLY) hald(2360)open("/dev/hdc",O_RDONLY|O_EXCL|O_NONBLOCK) ...

Embedded Labworks

KERNEL TRACEPOINTS

Kprobes possibilitam instrumentar qualquer instruo do kernel, mas adicionam um overhead ao processamento pelo fato de trabalharem com interrupes de software. Uma soluo mais leve so os tracepoints, que adicionam ponto de trace estticos no kernel, definidos em tempo de compilao. A implementao bem simples: se uma funo de probe estiver associada determinado tracepoint, esta funo ser chamada. Mais informaes sobre tracepoints nos fontes do kernel em Documentation/trace/tracepoints.txt .

Embedded Labworks

LTTng

Toolkit que permite coletar e analisar informaes de tracing do kernel, baseado em kernel tracepoints. At ento (Linux 3.6), necessrio aplicar patches no kernel para utilizar esta ferramenta de tracing. Capacidade de gerar timestamps extremamente precisos e com muito pouco overhead. Mais informaes na documentao do projeto: http://lttng.org/documentation

Embedded Labworks

LTTV VIEWER

Embedded Labworks

Linux Device Drivers

Profiling

Embedded Labworks

OPROFILE

O principal objetivo de uma ferramenta de profiling analisar a performance do sistema (consumo de ciclos de CPU). A ferramenta OProfile usa eventos de timer ou contadores de performance providos pelo processador para capturar dados em intervalos regulares. Mais informaes sobre o projeto em: http://oprofile.sourceforge.net/

Embedded Labworks

OPROFILE (EXEMPLO)
$opreportexcludedependent CPU:PIII,speed863.195MHz(estimated) CountedCPU_CLK_UNHALTEDevents(clocksprocessorisnot halted)withaunitmaskof0x00(Nounitmask)count50000 45038575.6634cc1plus 6021310.1156lyx 293134.9245XFree86 116331.9543as 102041.7142oprofiled 72891.2245vmlinux 70661.1871bash 64171.0780oprofile 63971.0747vim 30270.5085wineserver 11650.1957kdeinit 8320.1398wine ...

Embedded Labworks

LABORATRIO

Usando ferramentas de debugging

Embedded Labworks

Linux Device Drivers

Unified Device Model

Embedded Labworks

UNIFIED DEVICE MODEL

Antes da verso 2.6, o kernel no possuia um padro unificado de desenvolvimento de drivers que descrevesse os dispositivos conectados ao sistema e sua topologia. A partir do kernel 2.6 foi implementado o modelo de dispositivos unificado (unified device model), que basicamente um framework para o desenvolvimento de drivers para os diferentes tipos de dispositivos suportados pelo Linux.

Embedded Labworks

ALGUMAS VANTAGENS

Padro para o desenvolvimento de drivers, evitando duplicao de cdigo. Capacidade de identificar todos os dispositivos disponveis no sistema, e em qual barramento eles esto conectados. Capacidade de ligar dispositivos e drivers. Capacidade de dividir os dispositivos por classes, sem se importar com sua topologia fsica. Facilita o gerenciamento de energia (conhecendo o topologia do sistema, fica simples identificar qual dispositivo desligar primeiro).

Embedded Labworks

ARQUITETURA

A implementao do device model esta baseada em dois componentes:

Framework de drivers: como o dispositivo se apresenta para a camada de usurio. Infraestrutura de barramento: como o driver conversa com o dispositivo de hardware.

Embedded Labworks

ARQUITETURA (cont.)
User space Bibliotecas/aplicaes

Interface de chamada de sistema Framework Driver Infraestrutura de barramento

Kernel space

Hardware

Embedded Labworks

FRAMEWORK DE DRIVERS

Muitos drivers no so implementados diretamente como um driver de dispositivo de caractere. Eles so implementados sob um framework especfico de um tipo de dispositivo (exemplos: framebuffer, tty, input, etc). Vantagens do uso de um framework:

Padro (API comum) para o desenvolvimento de um tipo de driver. A mesma interface provida para as aplicaes, independente do driver.

Embedded Labworks

FRAMEWORKS
Aplicao Aplicao Interface de chamada de sistema Aplicao

Char driver

Framebuffer core

Input core

TTY core

Block core

Framebuffer driver

Input driver

TTY driver

Serial core

IDE core

SCSI core

Serial driver

IDE driver

SCSI driver

Embedded Labworks

EXEMPLO: FRAMEBUFFER

o framework padro para dispositivos de vdeo (monitor, display LCD, etc). habilitado na opo do kernel CONFIG_FB. Fontes disponveis em drivers/video/ e definio da API em <linux/fb.h>. Implementa um dispositivo de caractere (/dev/fbX), onde as aplicaes podem ler, escrever e enviar comandos ioctl para o dispositivo.

Embedded Labworks

EXEMPLO: FRAMEBUFFER (cont.)

Tudo o que um driver de framebuffer precisa fazer :

Definir e inicializar uma estrutura do tipo fb_info. Prover um conjunto de operaes que podem ser realizadas no dispositivo atravs da implementao da estrutura fb_ops. Registrar de register_framebuffer(). o dispositivo framebuffer com a funo

Existe um modelo de driver de framebuffer disponvel em drivers/video/skeletonfb.c.

Embedded Labworks

STRUCT FB_OPS
#include<linux/fb.h> staticstructfb_info*info; staticstructfb_opsxxxfb_ops={ .owner=THIS_MODULE, .fb_open=xxxfb_open, .fb_read=xxxfb_read, .fb_write=xxxfb_write, .fb_release=xxxfb_release, .fb_blank=xxxfb_blank, .fb_fillrect=xxxfb_fillrect, .fb_copyarea=xxxfb_copyarea, .fb_sync=xxxfb_sync, .fb_ioctl=xxxfb_ioctl, .fb_mmap=xxxfb_mmap, [...] };

Embedded Labworks

REGISTRANDO O FRAMEBUFFER
staticint__devinitxxxfb_probe(structpci_dev*dev, conststructpci_device_id*ent) { [...] info=framebuffer_alloc(sizeof(structxxx_par), device); info>fbops=&xxxfb_ops; if(register_framebuffer(info)<0) returnEINVAL; [...] }

Embedded Labworks

A CAMADA TTY

o framework padro para dispositivos de comunicao serial (porta serial, conversor USB/serial, etc). Fontes disponveis em drivers/tty/ e definio da API em <linux/tty.h>. Implementa um dispositivo de caractere (normalmente /dev/ttyXX), onde as aplicaes podem ler, escrever e enviar comandos ioctl para o dispositivo.

Embedded Labworks

A CAMADA TTY (cont.)

A camada TTY no Linux possui quatro componentes principais:

Driver de baixo nvel: conversa diretamente com o dispositivo de hardware. Driver TTY: faz interface do driver de baixo nvel com o core TTY, provendo uma interface comum de acesso ao dispositivo serial. Core TTY: camada de abstrao entre user space (bibliotecas e aplicaes) e o driver TTY. Line discipline: permite com que o core TTY manipule os dados enviados e recebidos pelo usurio de diferentes formas (terminal, conexo PPP, etc).

Embedded Labworks

A CAMADA TTY (cont.)


User space /dev/tty*

TTY Core Kernel space TTY driver

Line Discipline

Low level driver

Hardware

Dispositivo serial

Embedded Labworks

VANTAGENS DA CAMADA TTY

Esta arquitetura permite que possamos usar a camada TTY para:

Executar uma sesso de terminal atravs de uma conexo RS232. Se conectar Internet via modem dial-up. Se comunicar com dispositivos infravermelho. Emular uma porta serial atravs de um conversor USB/serial. Se comunicar atravs de uma porta RS485. Se conectar remotamente uma mquina via Telnet ou SSH. Etc!

Embedded Labworks

DISPOSITIVOS NA CAMADA TTY


User space
/dev/ttyS* /dev/ttyUSB* /dev/modem /dev/tty0..63

TTY Core
Kernel space

Line Disciplines

Serial TTY driver Driver Driver

USB/Serial TTY driver

Modem TTY driver

Virtual Terminal

Input

Driver

Driver

Driver

Driver

Hardware

Porta RS232

Porta RS485

Conversor USB/Serial

Modem PCMCIA

Monitor

Teclado

Embedded Labworks

UART DRIVER

Portanto, para ser integrado ao kernel, um driver de porta serial precisa fazer parte da camada TTY. Levando em considerao os quatro componentes da camada TTY, isso significa que:

Driver de baixo nvel: precisa ser desenvolvido. Driver TTY: j existe uma implementao do driver TTY para portas seriais chamado serial core. O driver de baixo nvel dever se registrar neste driver TTY. Core TTY: padro, no precisa mexer. Line discipline: tambm padro, e o comum usar N_TTY para que a porta serial funcione como um terminal.

Embedded Labworks

UART DRIVER (cont.)


User space /dev/ttyS*

tty_io.c Kernel space serial_core.c

n_tty.c

UART driver

Hardware

UART

Embedded Labworks

IMPLEMENTANDO DRIVER UART

Definir uma estrutura do tipo uart_driver que representar o driver. Definir uma estrutura do tipo uart_port para cada porta serial presente no sistema. Definir uma estrutura do tipo uart_ops com as operaes que podem ser realizadas na porta serial, e criar o link com a estrutura
uart_port.

Embedded Labworks

IMPLEMENTANDO DRIVER UART (cont.)

Registrar a estrutura uart_driver na inicializao do driver com uart_register_driver() e desregistrar esta estrutura na rotina de limpeza do driver com uart_unregister_driver(). Registrar a estrutura uart_port com uart_add_one_port() e desregistrar com uart_remove_one_port().

Embedded Labworks

EXEMPLO DRIVER UART


/*drivers/tty/serial21285.c*/ #include<linux/serial_core.h> staticstructuart_driverserial21285_reg={ .owner=THIS_MODULE, .driver_name="ttyFB", .dev_name="ttyFB", .major=SERIAL_21285_MAJOR, .minor=SERIAL_21285_MINOR, .nr=1, .cons=SERIAL_21285_CONSOLE, };

Embedded Labworks

EXEMPLO DRIVER UART (cont.)


/*UARToperations.Seedocumentationin Documentation/serial/driver*/ staticstructuart_opsserial21285_ops={ .tx_empty=serial21285_tx_empty, .get_mctrl=serial21285_get_mctrl, .set_mctrl=serial21285_set_mctrl, .stop_tx=serial21285_stop_tx, .start_tx=serial21285_start_tx, .stop_rx=serial21285_stop_rx, .enable_ms=serial21285_enable_ms, .break_ctl=serial21285_break_ctl, .startup=serial21285_startup, .shutdown=serial21285_shutdown, .set_termios=serial21285_set_termios, .type=serial21285_type, .release_port=serial21285_release_port, .request_port=serial21285_request_port, .config_port=serial21285_config_port, .verify_port=serial21285_verify_port, };

Embedded Labworks

EXEMPLO DRIVER UART (cont.)


staticstructuart_portserial21285_port={ .mapbase=0x42000160, .iotype=UPIO_MEM, .irq=0, .fifosize=16, .ops=&serial21285_ops, .flags=UPF_BOOT_AUTOCONF, };

Embedded Labworks

EXEMPLO DRIVER UART (cont.)


staticint__initserial21285_init(void) { [...] ret=uart_register_driver(&serial21285_reg); if(ret==0) uart_add_one_port(&serial21285_reg,&serial21285_port); [..] } staticvoid__exitserial21285_exit(void) { uart_remove_one_port(&serial21285_reg,&serial21285_port); uart_unregister_driver(&serial21285_reg); }

Embedded Labworks

LABORATRIO

Integrando driver com a camada TTY

Embedded Labworks

INFRAESTRUTURA DE BARRAMENTO

Os drivers dependem de uma infraestrutura de barramento que possa identificar, enumerar e se comunicar com os dispositivos conectados ao barramento. A infraestrutura de barramento composta por:

Um driver de barramento (bus driver), que implementa a API para um driver conversar com determinado barramento. Um driver adaptador (adapter driver), capaz de conversar fisicamente com o dispositivo atravs de determinado barramento (USB controllers, I2C adapters).

Embedded Labworks

INFRAESTRUTURA DE BARRAMENTO (cont.)


User space Bibliotecas/aplicaes Interface de chamada de sistema Kernel space Framework Driver Infraestrutura de barramento Hardware Bus driver Bus adapter

Embedded Labworks

INFRAESTRUTURA DE BARRAMENTO (cont.)

Existe um driver para cada tipo de barramento (USB, SPI, I2C, PCI, MMC, etc). Podem existir um ou mais drivers adaptadores, um para cada controlador de barramento existente no hardware. Para usar determinado barramento, os drivers adaptadores precisam se registrar no driver do barramento.

Embedded Labworks

DRIVER DE BARRAMENTO

O driver de barramento responsvel por:

Registrar o barramento, tambm chamado de core infrastructure, atravs da estrutura structbus_type. Permitir o registro de drivers adaptadores (USB controllers, I2C adapters, etc). Permitir o registro de drivers de dispositivo (USB devices, I2C devices, etc). Prover uma API tanto para os drivers de dispositivo quanto para os drivers adaptadores. Implementar as estruturas para a definio do dispositivo e do driver, tipicamente xxx_driver e xxx_device.

Embedded Labworks

EXEMPLO: BARRAMENTO I2C

Driver de barramento (I2C core):


drivers/i2c/i2ccore.c

Driver adaptador (I2C adapter para a linha i.MX da Freescale):


drivers/i2c/busses/i2cimx.c

Embedded Labworks

NA INICIALIZAO

Na inicializao do kernel, o adaptador I2C se registra no core I2C.

Core I2C
i2c_add_adapter() i2c-imx

Embedded Labworks

EXEMPLO DE DRIVER

Para ilustrar como os drivers so implementados, vamos estudar o driver do acelermetro MMA8450.

Ele um dispositivo conectado ao barramento I2C, portanto um driver I2C. Ele um dispositivo que gera eventos, portanto usa o framework de input do kernel.

Fontes disponveis em:


drivers/input/misc/mma8450.c

Embedded Labworks

IMPLEMENTANDO O DRIVER

Como este dispositivo esta conectado ao barramento I2C, ele deve implementar um driver I2C. Para isso, necessrio:

Definir uma estrutura do tipo structi2c_driver. Registrar o driver i2c com a funo i2c_add_driver() na inicializao do driver. Desregistrar o driver com a funo i2c_del_driver() na finalizao do driver.

Embedded Labworks

ESTRUTURA I2C
staticstructi2c_drivermma8450_driver={ .driver={ .name="mma8450", .owner=THIS_MODULE, }, .suspend=mma8450_suspend, .resume=mma8450_resume, .probe=mma8450_probe, .remove=__devexit_p(mma8450_remove), [...] };

Embedded Labworks

ADICIONANDO E REMOVENDO
staticint__initmma8450_init(void) { [...] res=i2c_add_driver(&mma8450_driver); [...] } staticvoid__exitmma8450_exit(void) { [...] i2c_del_driver(&mma8450_driver); [...] } module_init(mma8450_init); module_exit(mma8450_exit);

Embedded Labworks

NA INICIALIZAO

Na inicializao do kernel, o driver I2C se registra no core I2C.

Core I2C
i2c_add_driver() mma8450

Embedded Labworks

INSTANCIANDO O DRIVER

A partir deste momento, o barramento I2C sabe que existe um driver para tratar dispositivos do tipo mma8450. Mas para ser usado, o driver precisa ser instanciado. O que instanciar o driver? chamar sua funo probe(). Quando o driver instanciado? Quando o sistema identifica um dispositivo de hardware que pode ser tratado por aquele driver.

Embedded Labworks

INSTANCIANDO O DRIVER (cont.)

Como o driver pode ser instanciado?

Dinamicamente se o barramento possuir suporte enumerao de dispositivos conectados a ele (ex: USB). Estaticamente atravs de uma funo disponibilizada pela infraestrutura do barramento. Estaticamente usando uma funcionalidade chamada de device tree.

Embedded Labworks

INSTANCIANDO O DRIVER ESTATICAMENTE


/*arch/arm/machimx/machmx53_loco.c*/ staticstructi2c_board_infomx53loco_i2c_devices[]={ { I2C_BOARD_INFO("mma8450",0x1C), }, }; staticvoid__initmx53_loco_board_init(void) { [...] i2c_register_board_info(0,mx53loco_i2c_devices, ARRAY_SIZE(mx53loco_i2c_devices)); [...] }

Embedded Labworks

DEVICE PROBE
Driver mma8450
i2c_add_driver()

Definio do dispositivo I2C


i2c_register_board_info()

Core I2C
mma8450_probe()

Driver mma8450

Embedded Labworks

O MTODO PROBE

O mtodo probe() recebe como argumento uma estrutura descrevendo o dispositivo de acordo com o barramento (i2c_client, pci_dev, usb_interface, etc). Esta funo responsvel por:

Inicializar o dispositivo, mapear I/O em memria e registrar ISRs. A infraestrutura de barramento normalmente prov mecanismos para ler endereamento de I/O, nmero de interrupes e outras informaes especficas do dispositivo. Registrar o dispositivo no framework correto do kernel.

Embedded Labworks

MMA8450 PROBE
staticint__devinitmma8450_probe( structi2c_client*client, conststructi2c_device_id*id) { [...] result=i2c_smbus_read_byte_data(client, MMA8450_WHO_AM_I); [...] result=input_register_polled_device(idev); [...] }

Embedded Labworks

O MODELO RECURSIVO!
ALSA Network stack Input framework Input driver I2C device driver I2C core Network driver USB device driver I2C adapter driver USB device driver

USB core ALSA driver PCI device driver PCI core PCI Adapter USB Adapter driver PCI device driver

Embedded Labworks

PLATFORM DEVICES

Alguns dispositivos podem no estar conectados em um barramento, como por exemplo uma UART ou algum dispositivo conectado um GPIO. E como prover a mesma soluo sem uma infraestrutura de barramento? Criando a sua prpria infraestrutura de barramento! Esta implementao realizada atravs da infraestrutura de platform device e platform driver.

Embedded Labworks

IMPLEMENTANDO PLATFORM DRIVERS

Um dispositivo de hardware representado por um platform device e o driver para tratar este dispositivo representado por um platform driver. Para implementar um platform driver, necessrio:

Definir uma estrutura do tipo structplatform_driver. Registrar driver com a funo platform_driver_register() na inicializao do driver. Desregistrar com a funo platform_driver_unregister() na rotina de limpeza do driver. o driver o platform

Embedded Labworks

EXEMPLO PORTA SERIAL


/*drivers/tty/serial/imx.c*/ #include<linux/platform_device.h> staticstructplatform_driverserial_imx_driver={ .probe=serial_imx_probe, .remove=serial_imx_remove, .driver={ .name="imxuart", .owner=THIS_MODULE, }, [...] };

Embedded Labworks

EXEMPLO PORTA SERIAL (cont.)


/*drivers/tty/serial/imx.c*/ staticint__initimx_serial_init(void) { [...] ret=platform_driver_register(&serial_imx_driver); [...] } staticvoid__exitimx_serial_exit(void) { [...] platform_driver_unregister(&serial_imx_driver); [...] }

Embedded Labworks

INSTANCIANDO PLATFORM DRIVER

Como a infraestrutura de platform devices no possui um mecanismo de hotplug, um platform driver pode ser instanciado de duas formas:

Estaticamente atravs da criao e inicializao de uma estrutura do tipo platform_device. Estaticamente atravs da funcionalidade de device tree.

Embedded Labworks

DEFININDO UM PLATFORM DEVICE


staticstructplatform_deviceimx_uart1_device={ .name="imxuart", .id=0, .num_resources=ARRAY_SIZE(imx_uart1_resources), .resource=imx_uart1_resources, .dev={ .platform_data=&mxc_ports[0], } };

Embedded Labworks

INSTANCIANDO UM PLATFORM DEVICE


staticstructplatform_device*devices[]__initdata={ &cs89x0_device, &imx_uart1_device, &imx_uart2_device, }; staticvoid__initmx1ads_init(void) { [...] platform_add_devices(devices,ARRAY_SIZE(devices)); [...] }

Embedded Labworks

PLATFORM DEVICE PROBE


Platform Driver imx-uart
platform_driver_register()

Platform Device UART 1


platform_add_devices()

Platform device infrastructure


serial_imx_probe() Platform Driver imx-uart

Embedded Labworks

USANDO RECURSOS

Todo driver normalmente usa um ou mais recursos de hardware, como portas de I/O, linhas de interrupo ou canais de DMA. Estas informaes podem ser representadas em uma estrutura do tipo structresource, e um vetor de estruturas deste tipo esto associadas um platform device. Este tipo de mecanismo permite que determinado driver possa ser instanciado para gerenciar mltiplos dispositivos, que usam recursos de hardware diferentes, sem que uma linha de cdigo seja alterada.

Embedded Labworks

USANDO RECURSOS (cont.)


staticstructplatform_deviceimx_uart1_device={ .name="imxuart", .id=0, .num_resources=ARRAY_SIZE(imx_uart1_resources), .resource=imx_uart1_resources, .dev={ .platform_data=&mxc_ports[0], } }; Definio dos recursos usados pelo dispositivo de hardware

Embedded Labworks

USANDO RECURSOS (cont.)


staticstructresourcemxc_uart_resources1[]={ { .start=UART1_BASE_ADDR, .end=UART1_BASE_ADDR+0x0B8, .flags=IORESOURCE_MEM, }, { .start=MXC_INT_UART1, .flags=IORESOURCE_IRQ, }, };

Embedded Labworks

USANDO RECURSOS (cont.)


staticintserial_imx_probe(structplatform_device*pdev) { structresource*res; [...] /*getandremapmemorymappedI/O*/ res=platform_get_resource(pdev,IORESOURCE_MEM,0); base=ioremap(res>start,PAGE_SIZE); [...] /*getISRnumber*/ sport>rxirq=platform_get_irq(pdev,0); [...] }

Embedded Labworks

PLATFORM DATA

Alm dos recursos de hardware comuns que podem ser alocados para um determinado dispositivo, muitos dispositivos requerem informaes especficas que no esto disponveis em outros dispositivos de hardware (ex: modos de vdeo de um display). Estas informaes podem ser passadas para um driver em uma outra estrutura chamada de platform_data. Esta estrutura nada mais do que um ponteiro para void dentro da estrutura platform_device.

Embedded Labworks

USANDO RECURSOS (cont.)


staticstructplatform_deviceimx_uart1_device={ .name="imxuart", .id=0, .num_resources=ARRAY_SIZE(imx_uart1_resources), .resource=imx_uart1_resources, .dev={ .platform_data=&mxc_ports[0], } };

Definio de informaes especficas do dispositivo de hardware

Embedded Labworks

PLATFORM DATA (cont.)


staticuart_mxc_portmxc_ports[]={ [0]={ [...] .ints_muxed=1, .mode=MODE_DCE, .ir_mode=NO_IRDA, .enabled=1, .cts_threshold=UART1_UCR4_CTSTL, .dma_enabled=UART1_DMA_ENABLE, .dma_rxbuf_size=UART1_DMA_RXBUFSIZE, .rx_threshold=UART1_UFCR_RXTL, .tx_threshold=UART1_UFCR_TXTL, [...] }

Embedded Labworks

PLATFORM DATA (cont.)


staticintserial_imx_probe(structplatform_device*pdev) { [...] mxc_ports[id]=pdev>dev.platform_data; [...] if(mxc_ports[id]>enabled==1){ [...] } }

Embedded Labworks

LABORATRIO

Implementando um platform driver

Embedded Labworks

Linux Device Drivers

Frameworks

Embedded Labworks

RELEMBRANDO...
User space Bibliotecas/aplicaes

Interface de chamada de sistema Framework Driver Infraestrutura de barramento

Kernel space

Hardware

Embedded Labworks

TTY

O que ? Subsistema para dispositivos seriais. Quem usa? Porta serial RS232, porta serial RS485, conversor USB/Serial, etc. Interface: /dev/tty*. Fontes: drivers/tty/. Documentao: Documentation/serial/.

Embedded Labworks

INPUT

O que ? Subsistema para dispositivos de entrada. Quem usa? Mouse, teclado, touch screen, acelermetro, boto, etc. Interface: /dev/input/event*. Fontes: drivers/input/. Documentao: Documentation/input/.

Embedded Labworks

FRAMEBUFFER

O que ? Subsistema de vdeo. Quem usa? Controladores de vdeo em geral para conexo com monitores e displays. Interface: /dev/fb* e /sys/class/graphics/fb*/. Fontes: drivers/video/. Documentao: Documentation/fb/.

Embedded Labworks

ALSA

O que ? Subsistema de som (Advanced Linux Sound Architecture). Quem usa? Controladores de udio, placas de som, etc. Interface: /dev/snd/*. Fontes: sound/. Documentao: Documentation/sound/.

Embedded Labworks

V4L2

O que ? Interface para dispositivos de captura de udio e vdeo. Quem usa? Webcam, sintonizador de TV, receptor de rdio, receptor de TV digital, etc. Interface: /dev/dvb/* e /dev/v4l/*. Fontes: drivers/media/. Documentao: Documentation/video4linux/.

Embedded Labworks

BLOCK LAYER

O que ? Subsistema para dispositivos de armazenamento com capacidade de acesso randmico. Quem usa? Discos rgido, CD/DVD, pendrive, etc. Interface: /dev/sd*, /dev/sr*, etc. Fontes: drivers/block/. Documentao: Documentation/block/.

Embedded Labworks

MTD

O que ? Subsistema para memrias flash. Quem usa? Memrias flash NAND e NOR. Interface: /dev/mtd* e /dev/mtdblock*. Fontes: drivers/mtd/. Documentao: Documentation/mtd/.

Embedded Labworks

NETWORK

O que ? Subsistema de rede TCP/IP. Quem usa? Placas de rede em geral. Interface: Socket. Fontes: net/ e drivers/net/. Documentao: Documentation/networking/.

Embedded Labworks

BLUETOOTH

O que ? Subsistema para dispositivos bluetooth. Quem usa? Dispositivos bluetooth em geral. Interface: /dev/rfcomm* para emulao serial, interface de rede ppp*, /dev/input/event* para dispositivo de entrada, etc. Fontes: drivers/bluetooth/. Documentao: -

Embedded Labworks

NFC

O que ? Subsistema para dispositivos NFC. Quem usa? Dispositivos NFC em geral. Interface: Socket. Fontes: net/nfc/ e drivers/nfc/. Documentao: Documentation/nfc/.

Embedded Labworks

IrDA

O que ? Subsistema para comunicao via infravermelho. Quem usa? Dispositivos de comunicao infravermelho. Interface: /dev/rfcomm* para emulao serial, interface de rede ppp*, etc. Fontes: drivers/net/irda/. Documentao: Documentation/networking/irda.txt .

Embedded Labworks

HWMON

O que ? Subsistema para dispositivos de monitoramento do hardware. Quem usa? Sensores em geral (temperatura, corrente, tenso, etc), ventoinha, etc. Interface: /sys/class/hwmon/hwmon*/. Fontes: drivers/hwmon/. Documentao: Documentation/hwmon/.

Embedded Labworks

IIO (INDUSTRIAL I/O)

O que ? Subsistema (recente) para dispositivos conversores analgico/digital. Quem usa? Conversor A/D e D/A, acelermetro, sensor de luz, sensor de proximidade, compasso, giroscpio, magnetrmetro, etc. Interface: /sys/bus/iio/devices/*. Fontes: drivers/iio/. Documentao: drivers/staging/iio/Documentation/ .

Embedded Labworks

WATCHDOG

O que ? Subsistema para dispositivos watchdog. Quem usa? Qualquer chip com funcionalidade de watchdog. Interface: /dev/watchdog. Fontes: drivers/watchdog/. Documentao: Documentation/watchdog/.

Embedded Labworks

RTC

O que ? Subsistema para relgios de tempo real. Quem usa? RTCs em geral. Interface: /dev/rtc* e /sys/class/rtc/rtc*/. Fontes: drivers/rtc/. Documentao: Documentation/rtc.txt.

Embedded Labworks

PWM

O que ? Subsistema (recente) para dispositivos PWM. Quem usa? Qualquer chip com funcionalidade de PWM. Interface: /sys/class/pwm/*. Fontes: drivers/pwm/. Documentao: Documentation/pwm.txt.

Embedded Labworks

PINCTRL (PIN CONTROL)

O que ? Subsistema para gerenciar pinos de I/O. Quem usa? Toda e qualquer CPU ou SoC que possuir pinos de I/O para serem gerenciados, como o MUX presente nos SoCs atuais. Interface: Fontes: drivers/pinctrl/. Documentao: Documentation/pinctrl.txt.

Embedded Labworks

GPIO

O que ? Subsistema para gerenciar GPIOs. Quem usa? Toda e qualquer CPU ou SoC com GPIOs disponveis, expansor de I/O, shift register, etc. Interface: /sys/class/gpio/*. Fontes: drivers/gpio/. Documentao: Documentation/gpio.txt.

Embedded Labworks

LEDS

O que ? Subsistema para gerenciar leds. Quem usa? Todo e qualquer led disponvel no hardware. Interface: /sys/class/leds/*. Fontes: drivers/leds/. Documentao: Documentation/leds/.

Embedded Labworks

PARPORT

O que ? Subsistema para portas paralelas. Quem usa? Qualquer dispositivo de porta paralela. Interface: /dev/lp*. Fontes: drivers/parport/. Documentao: Documentation/parport.txt.

Embedded Labworks

CLOCK

O que ? Subsistema para gerenciar as fontes de clock do sistema. Quem usa? Todo e qualquer device driver que dependa de um clock para seu funcionamento (Ex: UART). Interface: Fontes: drivers/clk/. Documentao: Documentation/clk.txt.

Embedded Labworks

CPUFREQ

O que ? Subsistema para gerenciamento de energia. Quem usa? Sistema (CPU, SoC, etc). Interface: /sys/devices/system/cpu/cpu*/cpufreq/ . Fontes: drivers/cpufreq/ ou arch/<arch>/*. Documentao: Documentation/cpufreq/.

Embedded Labworks

POWER MANAGEMENT

O que ? Subsistema para gerenciar o estado de gerenciamento de energia do sistema (standby, suspend-to-RAM, suspend-to-disk). Quem usa? Toda plataforma que deseje este tipo de suporte. Interface: /sys/power/. Fontes: drivers/base/power/. Documentao: Documentation/power/.

Embedded Labworks

CPU IDLE

O que ? Subsistema para gerenciar o modo idle da CPU. Quem usa? Qualquer CPU que possua um ou mais modos idle. Interface: /sys/devices/system/cpu/cpu*/cpuidle/ . Fontes: drivers/cpuidle/. Documentao: Documentation/cpuidle/.

Embedded Labworks

POWER SUPPLY

O que ? Subsistema para fontes de energia. Quem usa? Dispositivos de controle de bateria, fontes de alimentao, etc. Interface: /sys/class/power_supply/*. Fontes: drivers/power/. Documentao: Documentation/power/power_supply_class.txt.

Embedded Labworks

REGULATOR

O que ? Subsistema para reguladores de corrente e tenso. Quem usa? Qualquer chip regulador de corrente e tenso. Interface: /sys/class/regulator/*. Fontes: drivers/regulator/. Documentao: Documentation/power/regulator/.

Embedded Labworks

MFD

O que ? Subsistema para chips multifuncionais. Quem usa? Qualquer chip com mltiplas funes. Interface: Fontes: drivers/mfd/. Documentao: -

Embedded Labworks

SEM FRAMEWORK?

So raros os casos onde um dispositivo no se encaixa em nenhum destes frameworks. Mas estes casos ainda existem. Exemplos:

Display de 7 segmentos. Display LCD 2x20. EEPROM.

Para estes casos, temos duas solues:

Criar uma interface especfica no /sys. Criar um misc driver com interface no /dev.

Embedded Labworks

LABORATRIO

Analisando a implementao de drivers

Embedded Labworks

Linux Device Drivers

E agora?

Embedded Labworks

FILOSOFIA LINUX (1)

"Linux is evolution, not intelligent design." Linus Torvalds

Embedded Labworks

FILOSOFIA LINUX (2)

"Talk is cheap. Show me the code." Linus Torvalds

Embedded Labworks

LEIA A DOCUMENTAO

uma extensa documentao Documentation/. Consulte sempre!

kernel

tem

no

diretrio

O kernel tambm possui um conjunto de documentos em Documentation/DocBook, que podem ser compilados com um dos comandos abaixo:
$makepdfdocs $makehtmldocs

Embedded Labworks

LEIA A DOCUMENTAO (cont.)

Alguns documentos interessantes:

Documentation/HOWTO: contm instrues gerais sobre como se

tornar um desenvolvedor do kernel.

Documentation/ManagementStyle :

descreve gerenciamento do Linux, e um pouco de sua cultura. Linux no tem uma interface (API) estvel.

estilo

de

Documentation/stable_api_nonsense.txt : explica porque o

Embedded Labworks

PROCESSO DE DESENVOLVIMENTO
-next Linus Torvalds Andrew Morton

Mantenedor do subsistema

Mantenedor do subsistema

Mantenedor do driver ou arquivo

Mantenedor do driver ou arquivo

Mantenedor do driver ou arquivo

Desenvolvedor

Desenvolvedor

Desenvolvedor

Desenvolvedor

Desenvolvedor

Desenvolvedor

Embedded Labworks

PROCESSO DE DESENVOLVIMENTO (cont.)

Boa documentao sobre o processo de desenvolvimento em Documentation/developmentprocess/ . A maioria das rvores git dos subsistemas do kernel encontram-se no link abaixo: http://git.kernel.org/ Cada subsistema possui uma lista de discusso e um ou mais mantenedores.

Embedded Labworks

PROCESSO DE DESENVOLVIMENTO (cont.)

O e-mail do mantenedor, bem como a lista de discusso do subsistema, esto disponveis no arquivo MAINTAINERS no diretrio principal do kernel. O script scripts/get_maintainer.pl pode te ajudar a identificar o e-mail da lista de discusso e o nome e e-mail dos mantenedores de determinado arquivo ou patch!

Embedded Labworks

GET MAINTAINER
$./scripts/get_maintainer.plfinit/main.c RustyRussell<rusty@rustcorp.com.au>(commit_signer:4/20=20%) JimCromie<jim.cromie@gmail.com>(commit_signer:3/20=15%) AlViro<viro@zeniv.linux.org.uk>(commit_signer:3/20=15%) "H.PeterAnvin"<hpa@linux.intel.com>(commit_signer:3/20=15%) AndrewMorton<akpm@linuxfoundation.org>(commit_signer:2/20=10%) linuxkernel@vger.kernel.org(openlist)

Embedded Labworks

CONTRIBUINDO

Todo o processo de contribuio acontece por e-mail atravs das listas de discusso. A principal lista de discusso a LKML (Linux Kernel Mailing List): http://lkml.org/ Mas muitos subsistemas possuem listas especficas. http://vger.kernel.org/vger-lists.html

Embedded Labworks

CONTRIBUINDO (cont.)

Para colaborar, s preparar o patch e enviar para a lista de discusso e para todos os mantenedores responsveis pelo subsistema. O gerenciamento dos patches enviados feito com a ferramenta patchwork. https://patchwork.kernel.org/

Embedded Labworks

CONTRIBUINDO (cont.)

Documentao sobre como submeter um patch:


Documentation/SubmittingPatches

Checklist adicional para submisso de patches:


Documentation/SubmitChecklist

Documentao especfica sobre como submeter novos drivers:


Documentation/SubmittingDrivers

Embedded Labworks

CONTRIBUINDO (cont.)
$gitclonegit://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git $cdlinux/ $gitbranchsmeagol $gitcheckoutsmeagol #changeLinux,andchangetheworld! $scripts/checkpatch.plf<changed_files> $gitcommita $gitformatpatchmaster..smeagol $scripts/get_maintainer.pl<patch_file> $gitsendemailtoemail@email.comccemail@email.com<patch_file>

Embedded Labworks

AGUARDANDO APROVAO

Ao enviar um patch, voc pode esperar por crticas, comentrios, pedidos para alterar o cdigo ou pedidos para explicar determinado ponto da alterao. Voc deve ser capaz de aceitas as crticas, analisar tecnicamente o problema, alterar o cdigo ou explicar claramente os motivos pelos quais o cdigo no foi alterado, e reenviar o patch. Se no receber resposta, espere alguns dias e reenvie o patch.

Embedded Labworks

AGUARDANDO APROVAO (cont.)

Publicly making fun of people is half the fun of open source programming. In fact, the real reason to eschew programming in closed environments is that you can't embarrass people in public. Linus Torvalds. um processo que exige muita pacincia e perseverana!

Embedded Labworks

PROCURANDO AJUDA

O website Kernel Newbies um ponto de referncia para buscar ajuda sobre o funcionamento interno do Kernel. http://kernelnewbies.org Possui uma lista de discusso e um canal IRC onde so discutidos qualquer tipo de assunto relacionado ao kernel, do mais bsico ao mais avanado. Lembre-se de procurar nos arquivos da lista antes de postar alguma pergunta!

Embedded Labworks

REPORTANDO PROBLEMAS

Um documento descrevendo como reportar bugs chamado REPORTINGBUGS encontra-se no diretrio principal do kernel. O kernel tambm tem um bug tracker no link abaixo: https://bugzilla.kernel.org Voc pode contribuir reportando ou corrigindo problemas!

Embedded Labworks

PARA COMEAR

Kernel Janitors um projeto do site Kernel Newbies para quem quiser aprender a desenvolver para o kernel revisando o cdigo, fazendo limpezas, convertendo o uso de APIs e dando manuteno em cdigo antigo. http://kernelnewbies.org/KernelJanitors Linux Driver Project um projeto criado para quem quiser ajudar no desenvolvimento de drivers. http://www.linuxdriverproject.org/

Embedded Labworks

LINKS

Site do kernel Linux: http://www.kernel.org Linux kernel mailing list: http://www.tux.org/lkml Acompanhar as mudanas nas novas verses do kernel: http://wiki.kernelnewbies.org/LinuxChanges Notcias e novidades sobre o desenvolvimento do kernel: http://lwn.net

Embedded Labworks

LIVROS LINUX KERNEL


Linux Kernel in a Nutshell Greg Kroah-Hartman

Linux Kernel Development Robert Love

Embedded Labworks

LIVROS LINUX DEVICE DRIVERS


Essential Linux Device Drivers Sreekrishnan Venkateswaran

Linux Device Drivers Jonathan Corbet & others

Embedded Labworks

BECOMING A MASTER

Leia muito cdigo! Desenvolva! Leia mais cdigo! Desenvolva! Leia muito mais cdigo! Contribua com a comunidade!

OBRIGADO!
E-mail Website sergio.prado@e-labworks.com http://e-labworks.com

Embedded Labworks

Por Sergio Prado. So Paulo, Novembro de 2012 Copyright Embedded Labworks 2004-2013. All rights reserved.

Você também pode gostar