Mini2440 – Compilando aplicações e device drivers

- por Sergio Prado

Categorias: Linux, Mini2440 Tags: , , ,

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

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.