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

Faça um Comentário

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