Mini2440 – Compilando aplicações e device drivers
- por Sergio Prado
Vimos um tempo atrás como gerar um Linux embarcado do zero com o Buildroot neste artigo aqui. Geramos o toolchain, a imagem do kernel e o rootfs. Mas compilamos apenas com os pacotes disponíveis por padrão no Buildroot. E se agora você quiser compilar uma aplicação ou um device driver e integrar na sua solução. Como podemos usar a infraestrutura gerada pelo Buildroot e cross-compilar qualquer código para ser executado na mini2440. Isso é o que veremos neste artigo.
Obs: Os termos cross-compiling, cross-compilar, cross-compilação e compilação cruzada tem o mesmo significado: compilar uma aplicação em uma arquitetura e gerar código para uma outra arquitetura. No nosso caso iremos compilar em um x86 e gerar código para um ARM.
Como pré-requisito, é necessário que você tenha executado todos os passos do artigo mencionado acima. Para realizar os testes, você pode configurar um ambiente com NFS, conforme expliquei neste artigo aqui.
Vamos utilizar o toolchain gerado pelo Buildroot. Você verá que o processo de compilação cruzada é bem simples, bastando apenas algumas alterações no Makefile do projeto.
COMPILANDO UMA APLICAÇÃO STANDALONE
Este é o código que iremos compilar para testar na mini2440:
1 2 3 4 5 6 7 8 9 10 |
/* hello.c */ #include "stdio.h" int main() { printf("Hello mini2440!\n"); return 0; } |
Se fossemos compilar normalmente em nossa máquina, este seria o Makefile que utilizaríamos:
all: @gcc hello.c -o hello clean: @rm -Rf *.o hello |
Porém, iremos cross-compilar. Isso significa que iremos utilizar um compilador diferente, e o código compilado será linkado com uma biblioteca diferente. Este será o nosso Makefile para cross-compilar a aplicação:
TOOLCHAIN=/opt/buildroot/buildroot-2010.08/output/staging/usr/bin/ CROSS_COMPILE=arm-linux- PATH := ${TOOLCHAIN}:${PATH} all: @${CROSS_COMPILE}gcc hello.c -o hello clean: @rm -Rf *.o hello |
A variável TOOLCHAIN vai depender do local onde você instalou o Buildroot. No meu caso, instalei em "/opt/buildroot/buildroot-2010.08". Ajuste de acordo com seu ambiente.
Veja que foram apenas 2 mudanças em relação ao Makefile anterior. Alteramos a variável de ambiente PATH para que o sistema encontre os binários do toolchain, e em vez de compilar com o "gcc", estamos usando o "arm-linux-gcc".
Os dois arquivos (hello.c e Makefile) devem estar no mesmo diretório. Para compilar, digite apenas "make". Após a compilação, você pode conferir se o binário foi compilado para a arquitetura correta com o comando "file":
$ file hello hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped |
Para testar, copie o binário para o sistema de arquivos que você configurou com o NFS:
$ sudo cp hello /home/mini2440/rootfs/usr/bin/ |
Agora, entre na console da mini2440 e teste sua aplicação:
$ /usr/bin/hello Hello mini2440! |
COMPILANDO UM DEVICE DRIVER
Para compilar um device driver não é muito diferente ou mais complicado.
Este é um "esqueleto" de um driver simples que escrevi, apenas com as funções de inicialização e finalização, mas que serve para nosso propósito de compilar e testar na mini2440:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
/* file leds.c */ #include "linux/fs.h" #include "linux/cdev.h" #include "linux/module.h" #include "linux/kernel.h" #include "linux/device.h" #include "asm/uaccess.h" #include "linux/slab.h" #define DEVICE_NAME "leds" #define NUM_LEDS 1 int __init leds_init(void); void __exit leds_exit(void); /* per led structure */ struct leds_device { int status; struct cdev cdev; } leds_dev[NUM_LEDS]; /* file operations structure */ static struct file_operations leds_fops = { .owner = THIS_MODULE, }; static dev_t leds_dev_number; /* driver initialization */ int __init leds_init(void) { int ret, i; /* request device major number */ if ((ret = alloc_chrdev_region(&leds_dev_number, 0, NUM_LEDS, DEVICE_NAME) < 0)) { printk(KERN_DEBUG "Error registering device!\n"); return ret; } /* init each led device */ for (i = 0; i < NUM_LEDS; i++) { /* init led status */ leds_dev[i].status = 0; /* connect file operations to this device */ cdev_init(&leds_dev[i].cdev, &leds_fops); leds_dev[i].cdev.owner = THIS_MODULE; /* connect major/minor numbers */ if ((ret = cdev_add(&leds_dev[i].cdev, (leds_dev_number + i), 1))) { printk(KERN_DEBUG "Error adding device!\n"); return ret; } } printk("Initializing leds driver.\n"); return 0; } /* driver exit */ void __exit leds_exit(void) { int i; /* delete devices */ for (i = 0; i < NUM_LEDS; i++) { cdev_del(&leds_dev[i].cdev); } /* release major number */ unregister_chrdev_region(leds_dev_number, NUM_LEDS); printk("Exiting leds driver.\n"); } module_init(leds_init); module_exit(leds_exit); MODULE_LICENSE("GPL"); |
Para compilar, este é o Makefile:
TOOLCHAIN := /opt/buildroot/buildroot-2010.08/output/staging/usr/bin/ KDIR := /opt/buildroot/buildroot-2010.08/output/build/linux-2.6/ PWD := $(shell pwd) PATH := $(TOOLCHAIN):${PATH} obj-m += leds.o default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: @rm -Rf *.o *.ko *.mod.c modules.order Module.symvers |
A variável KDIR deve apontar para o diretório do kernel que o Buildroot compilou para a mini2440. Ajuste-a de acordo com seu ambiente. O mesmo deve ser feito novamente com a variável TOOLCHAIN. Perceba que aqui não precisamos setar o compilador para "arm-linux-gcc". Este Makefile é uma extensão do Makefile do kernel, e como o kernel já foi compilado para esta arquitetura, ele sabe qual toolchain (compilador) usar.
Novamente, os dois arquivos (leds.c e Makefile) devem estar no mesmo diretório, e para compilar, digite apenas "make". O módulo gerado terá a extensão ".ko". Você também pode conferir se o módulo foi compilado para a arquitetura correta com o comando "file":
$ file leds.ko leds.ko: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped |
Agora, copie o binário para o sistema de arquivos que você configurou com o NFS:
$ sudo cp leds.ko /home/mini2440/rootfs/usr/bin/ |
E teste inserindo e removendo o driver:
$ insmod /usr/bin/leds.ko Initializing leds driver. $ rmmod leds Exiting leds driver. |
Claro que estes exemplos são bem simples, e o Makefile pode ficar bem complicado em projetos maiores. Mas os conceitos utilizados serão sempre os mesmos.
Um abraço,
Sergio Prado