Na parte 1 desta série de arti­gos, dei uma intro­dução sobre o desen­volvi­mento de dri­vers de dis­pos­i­tivos em Linux. Vimos detal­hes de um sis­tema com Linux embar­cado, a arquite­tura do ker­nel e como as apli­cações se comu­ni­cam com o hardware.

Neste artigo vamos final­mente colo­car a mão na massa e cod­i­ficar um dri­ver de dis­pos­i­tivo de car­ac­tere (char dri­ver). Desen­volver­e­mos o “Hello world!” do soft­ware embar­cado: acen­der­e­mos leds!

Usare­mos o kit mini2440, que pos­sui 4 leds disponi­bi­liza­dos nas por­tas de I/O GPB5 à GPB8. Cri­are­mos 4 arquivos de dis­pos­i­tivo (/dev/led1, /dev/led2, /dev/led3, /dev/led4) para geren­ciar o sta­tus destes leds. Enviar “1″ para “/dev/led1″ irá acen­der o led 1, e enviar “0″ irá apagá-lo.

ESQUELETO DE UM DEVICE DRIVER

Muito bem, chega de teo­rias e bla bla bla. Este é o esqueleto de um device driver:

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
85
86
87
88
#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 "asm/io.h"
#include "linux/slab.h"
 
#define DEVICE_NAME     "leds"
 
#define NUM_LEDS        4
 
#define LED_ON          1
#define LED_OFF         0
 
/* per led structure */
struct leds_device {
    int number;
    int status;
    struct cdev cdev;
} leds_dev[NUM_LEDS];
 
/* file operations structure */
static struct file_operations leds_fops = {
    .owner      = THIS_MODULE,
    .open       = leds_open,
    .release    = leds_release,
    .read       = leds_read,
    .write      = leds_write,
};
 
/* leds driver major number */
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 leds GPIO port */
    initLedPort();
 
    /* init each led device */
    for (i = 0; i < NUM_LEDS; i++) {
 
        /* init led status */
        leds_dev[i].number = i + 1;
        leds_dev[i].status = LED_OFF;
 
        /* 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;
        }
 
        /* init led status */
        changeLedStatus(leds_dev[i].number, LED_OFF);
    }
 
    printk("Leds driver initialized.\n");
 
    return 0;
}
 
/* driver exit */
void __exit leds_exit(void)
{
    /* 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 começar, veja que exis­tem duas funções definidas: leds_init(), que será chamada na ini­cial­iza­ção do dri­ver, ao ser car­regado para a memória; e leds_exit(), que por sua vez será chamada ao remover o driver.

Exis­tem tam­bém duas estru­turas definidas: leds_dev[] armazena as infor­mações de cada um dos 4 leds, e leds_fops armazena as oper­ações que poderão ser real­izadas com os leds (os nomes são auto-explicativos). Den­tro de leds_dev[], temos cdev, que é a estru­tura que rep­re­senta um char device.

A primeira tarefa que um dri­ver deve exe­cu­tar é definir o “major num­ber” que estará asso­ci­ado a ele. Ele pode req­ui­si­tar um deter­mi­nado número fixo com a função register_chrdev_region(), ou pedir para o ker­nel gerar um número dinami­ca­mente com a função alloc_chrdev_region(). Req­ui­si­tar um número fixo pode ser um risco pois não sabe­mos se ele já esta sendo usado, por­tanto a solução mais segura é requisitá-lo dinami­ca­mente (é o que faze­mos na linha 42).

Na chamada a alloc_chrdev_region(), req­ui­si­ta­mos ao ker­nel a alo­cação de 4 minor num­bers” (parâmetro 3), começando por “0″ (parâmetro 2) e armazenando o “major num­ber” em leds_dev_number (parâmetro 1). O quarto parâmetro diz ao ker­nel o nome asso­ci­ado ao dri­ver, que você poderá ver quando lis­tar o dis­pos­i­tivo em “/proc/devices”.

Na linha 51 temos um loop a ser exe­cu­tado para cada device (led) que cri­are­mos. Na linha 58, a função cdev_init() asso­cia a estru­tura do led “cdev” (parâmetro 1) às funções de acesso ao arquivo de dis­pos­i­tivo (parâmetro 2).

Na linha 62 é que a mág­ica acon­tece. A função cdev_add() asso­cia a estru­tura do led cdev (parâmetro 1) ao seu “major e minor num­ber” (para­metro 2).

Na prática: quando você real­iza uma oper­ação de escrita em /dev/led1 (major=253 e minor=0), o ker­nel procura a estru­tura cdev asso­ci­ada a este device (mesmo major/minor num­bers), que você definiu em cdev_add(), e então passa esta estru­tura como parâmetro para a função write(), que você asso­ciou na chamada a cdev_init(). Sim­ples, não?

Mas como é então a imple­men­tação das funções de oper­ação nos arquivos de dispositivo?

ACESSANDO O HARDWARE PELO ARQUIVO DE DISPOSITIVO

Não tem seg­redo, o con­ceito é simples:

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
/* open led file */
int leds_open(struct inode *inode, struct file *file)
{
    struct leds_device *leds_devp;
 
    /* get cdev struct */
    leds_devp = container_of(inode->i_cdev, struct leds_device, cdev);
 
    /* save cdev pointer */
    file->private_data = leds_devp;
 
    /* return OK */
    return 0;
}
 
/* close led file */
int leds_release(struct inode *inode, struct file *file)
{
    /* return OK */
    return 0;
}
 
/* read led status */
ssize_t leds_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
    struct leds_device *leds_devp = file->private_data;
 
    if (leds_devp->status == LED_ON) {
        if (copy_to_user(buf, "1", 1))
            return -EIO;
    }
    else {
        if (copy_to_user(buf, "0", 1))
            return -EIO;
    }
 
    return 1;
}
 
ssize_t leds_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
    struct leds_device *leds_devp = file->private_data;
    char kbuf = 0;
 
    if (copy_from_user(&kbuf, buf, 1)) {
        return -EFAULT;
    }
 
    if (kbuf == &#39;1&#39;) {
        changeLedStatus(leds_devp->number, LED_ON);
        leds_devp->status = LED_ON;
    }
    else if (kbuf == &#39;0&#39;) {
        changeLedStatus(leds_devp->number, LED_OFF);
        leds_devp->status = LED_OFF;
    }
 
    return count;
}

A função leds_open() é chamada na aber­tura do arquivo. Sua única respon­s­abil­i­dade, no nosso caso, é sal­var a estru­tura leds_dev asso­ci­ada ao device (led), pois usare­mos esta estru­tura nas funções de leitura e escrita. Isso é feito através da função container_of() na linha 7. O pon­teiro para leds_dev é salvo em file->private_data na linha 10.

Na função de leitura leds_read(), recu­per­amos a estru­tura do led leds_dev em file->private_data na linha 26, ver­i­fi­camos o staus do led e retor­namos “1″ para aceso e “0″ para apa­gado. Veja que, para retornar dados para a apli­cação, você pre­cisa usar a função copy_to_user(). Esta é uma forma do ker­nel enviar dados para a apli­cação, já que o ker­nel não “enx­erga” a memória dos proces­sos rodando em “user mode”, assim como a apli­cações não enx­ergam a memória dos proces­sos rodando em “ker­nel mode”.

Na função de escrita leds_write(), tam­bém recu­per­amos a estru­tura do led leds_dev em file->private_data na linha 42. E usamos tam­bém a função copy_from_user() na linha 45 para ler os dados rece­bidos da apli­cação. E então, depen­dendo dos dados rece­bidos (“0″ ou “1″), mudamos o estado dos leds.

Bom, agora falta o prin­ci­pal: as roti­nas de acesso às por­tas de I/O para geren­ciar os leds da mini2440.

ESCOVANDO UM POUCO DE BITS

Ire­mos aces­sar os leds disponíveis na porta GPB da CPU, con­forme con­fig­u­ração abaixo:

leds pinout Linux Device Drivers   Parte 2

Pre­cisamos con­fig­u­rar as por­tas como saída no reg­istrador GPBCON, e ligar/desligar os leds no reg­istrador GPBDAT. Os leds são aciona­dos em nível baixo.

Esta CPU (S3C2440) mapeia o acesso ao GPB através do endereço de memória 0x56000010. Porém, este é um endereço real, e no ker­nel você tra­bal­hará sem­pre com endereços vir­tu­ais. Olhando os fontes do ker­nel, ver­i­fiquei que o endereço vir­tual asso­ci­ado à este endereço real é o 0xFB000010. As definições estão no arquivo “arch/arm/mach-s3c2410/include/mach/regs-gpio.h”.

Vamos então dar uma olhada no código:

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
#define GPB_BASE    0xFB000010
#define GPBCON      GPB_BASE
#define GPBDAT      GPB_BASE + 4
 
#define CLEAR_PORT_MASK     0xFFFC03FF
#define SET_WRITE_PORT_MASK 0x00015400
 
/* initialize led port - GPB */
void initLedPort(void)
{
    void __iomem *base = (void __iomem *)GPBCON;
 
    u32 port = __raw_readl(base);
 
    port &= CLEAR_PORT_MASK;
    port |= SET_WRITE_PORT_MASK;
 
    __raw_writel(port, base);
}
 
/* change led status */
void changeLedStatus(int led_num, int status)
{
    void __iomem *base = (void __iomem *)GPBDAT;
    u32 mask, data;
 
    data = __raw_readl(base);
 
    mask = 0x01 << (4 + led_num);
 
    switch (status) {
 
        case LED_ON:
            mask = ~mask;
            data &= mask;
            break;
 
        case LED_OFF:
            data |= mask;
            break;
    }
 
    __raw_writel(data, base);
}

A função ini­tLed­Port() é chamada na carga do dri­ver e irá ini­cializar as por­tas dos leds (GPB5-GPB8) para tra­bal­har como saída através do reg­istrado GPBCON (bits 10..17). Na linha 15 zer­amos a con­fig­u­ração e na linha 16 con­fig­u­ramos as por­tas como saída.

Veja que esta­mos usando as macros __raw_readl() para ler um inteiro de 4 bytes da memória vir­tual e __raw_writel() para escr­ever um inteiro de 4 bytes na memória vir­tual. Esse mecan­ismo é mais seguro para aces­sar I/O mapeado em memória em Linux.

A função changeLed­Sta­tus() irá mudar o sta­tus do led para aceso ou apa­gado através do reg­istrador GPBDAT (bits 5..8).

COMPILANDO E TESTANDO

O código-fonte com­pleto e o Make­file podem ser baix­a­dos aqui. Para com­pi­lar, basta seguir os pro­ced­i­men­tos que descrevi neste artigo.

Para trans­ferir para a mini2440, você pode colo­car a placa em rede com seu PC, e usar alguma apli­cação de trans­fer­ên­cia de arquivos como o scp ou o ftp. Você pode tam­bém con­fig­u­rar um sis­tema de arquivos com NFS, con­forme descrevi aqui.

Alguns dos coman­dos que usare­mos para testes assumem que o módulo com­pi­lado (leds.ko) foi copi­ado para a mini2440 no diretório abaixo:

/lib/modules/2.6.32.2-FriendlyARM/kernel/drivers/char/

Por­tanto, adapte os coman­dos se necessário.

O primeiro passo é ini­ciar o dri­ver. Uma men­sagem será exibida indi­cando sucesso na ini­cial­iza­ção e todos os leds serão apagados.

$ insmod /lib/modules/2.6.32.2-FriendlyARM/kernel/drivers/char/leds.ko
Leds driver initialized.

Agora você pre­cisa ver­i­ficar o “major num­ber” alo­cado pelo ker­nel para o device dri­ver usando o comando abaixo (veja que, no meu caso, foi alo­cado o número 253):

$ grep leds /proc/devices
253 leds

Com o “major num­ber” em mãos, cri­are­mos todos os arquivos de dis­pos­i­tivo com os coman­dos abaixo (mude o valor do major num­ber se necessário):

$ mknod /dev/led1 c 253 0
$ mknod /dev/led2 c 253 1
$ mknod /dev/led3 c 253 2
$ mknod /dev/led4 c 253 3
$ chmod a+w /dev/led*

Obs: No próx­imo artigo ver­e­mos como usar um mecan­ismo de hot­plug para cri­ação dinâmica de arquivos de dispositivo.

Agora é hora de tes­tar. Para acen­der um led, basta enviar o car­ac­ter “1”. Os coman­dos abaixo acen­dem os leds 1 e 3:

$ echo "1" > /dev/led1
$ echo "1" > /dev/led3

E para apa­gar um led, basta enviar “0”:

$ echo "0" > /dev/led3

Para ver­i­ficar o estado de um led, não podemos ape­nas ler o arquivo com o comando “cat” por exem­plo. Isso porque na rotina que trata a leitura do arquivo, perceba que nunca retor­namos EOF (end-of-file). Isso sig­nifica que, se por exem­plo o led estiver aceso, o comando “cat /dev/led1″ irá imprimir indefinida­mente o número “1″. Então, para ler o estado do led, pre­cisamos de um comando que leia ape­nas 1 byte. Podemos fazer isso com o comando “dd”, sal­var o byte em um arquivo, e depois imprimir, con­forme abaixo:

$ dd if=/dev/led1 of=/tmp/led bs=1 count=1
$ echo "LED=$(cat /tmp/led)"
LED=1

E final­mente, para remover o driver:

$ rmmod leds
Exiting leds driver.

PRÓXIMOS PASSOS

Muito bem, vocês viram que o assunto deste artigo foi bem denso. Se para você foi muita infor­mação de uma só vez, pare, respire, pense. Estude o código com mais calma, e você verá que não existe nen­hum seg­redo no desen­volvi­mento de device dri­vers para Linux. Basta con­hecer a API do Linux e esco­var um pouco de bits.

No próx­imo artigo vamos apren­der alguns novos con­ceitos, como cri­ação dinâmica de arquivos de dis­pos­i­tivo, geren­ci­a­mento do hard­ware através do diretório “/sys” e roti­nas de timer do ker­nel (vamos adi­cionar uma fun­cional­i­dade de pisca-pisca ao nosso driver).

Um abraço!

Ser­gio Prado

VN:F [1.9.17_1161]
Rat­ing: 9.8/10 (12 votes cast)
Linux Device Dri­vers — Parte 2, 9.8 out of 10 based on 12 ratings

Posts rela­ciona­dos:

  1. Mini2440 — Com­pi­lando apli­cações e device drivers
  2. Linux Device Dri­vers — Parte 1
  3. Mini2440 — Linux com U-Boot e Emdebian
  • Juliano

    Eai Ser­gio,
     
    Parabens por mais um exce­lente artigo.Tenho uma pergunta,eu posso usar os device dri­vers linka­dos em uma apli­cação feita no QT??

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org ser­gio­prado

      Olá Juliano!

      Não sei se entendi dire­ito o que você quis dizer com “linkado”. Você não con­segue com­pi­lar e linkar o device dri­ver com o binário da sua apli­cação pelos motivos que expliquei no artigo: qual­quer processo que roda em user space não tem acesso ao ker­nel space. Você pre­cisa compilá-los sep­a­rada­mente. O acesso ao device dri­ver será mesmo ape­nas pela inter­face provida em /dev/ledX.

      Um abraço!

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • http://www.sirandsir.com Mar­cos

    Fala Sér­gio,
       Cara exce­lente artigo! Essa série está bem bacana. Eu não vejo a hora dos próx­i­mos arti­gos. Parabéns!
    um abraço,
    Marcos

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
  • http://www.fdavid.com.br Fran­cis David

    Linux embar­cado tem o /proc/cpuinfo, gostaria de saber quanto um ATMega ou outro micro­proces­sador tem de bogomips.

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org ser­gio­prado

      Olá Fran­cis!

      Os dois coman­dos abaixo devem te retornar o valor do bogomips:

      grep –i bogomips /proc/cpuinfo
      dmesg | grep –i bogomips

      Se pos­sível, use o valor de /proc/cpuinfo. As men­sagens do ker­nel retor­nadas em dmesg são sal­vas em um buffer cir­cu­lar, então depen­dendo do uptime do seu SO, elas já podem ter sido sobre­screv­i­das por out­ras mensagens.

      Um abraço!

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • Marcelo

      Olá Sér­gio!

      Parabéns pelos arti­gos! Estarei sem­pre acom­pan­hando, e acred­ito que os próx­i­mos capí­tu­los serão bem interessantes!

      Um grande abraço
      Marcelo

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
  • kle­ber povoação

    show de bola o artigo.…

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
  • Daniel

    define GPB_BASE 0xFB000010

    Não teria alguma forma de deter­mi­nar isso dinami­ca­mente?
    Parece que isso pode causar prob­lema futu­ra­mente, do tipo:
    “tro­cou a placa por uma ver­são mais nova e os LED pararam de fun­cionar!“
    Ou talvez uma atu­al­iza­ção de ver­são do kernel…

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org ser­gio­prado

      Olá Daniel!

      Você esta certís­simo! Usei o valor direto pois achei que facil­i­taria a leitura. O mais cor­reto seria usar a função S3C24XX_GPIO_BASE() para ler o endereço vir­tual base da porta de I/O. Se você seguir esta definição, verá que o mapea­mento de memória esta definido em “arch/arm/mach-s3c2400/include/mach/map.h”.

      Um abraço!

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • Renato

    Olá Sér­gio,

    Parabéns pelo artigo, seu blog está me abrindo os olhos para este mundo fasci­nante dos sis­temas embar­ca­dos, obri­gado!
     
    Tenho uma dúvida, como você desco­briu o endereço vir­tual mapeado para o endereço lógico do gpio? Posso fazer uso das estru­turas e funções definidas pela <linux/mm.h> para desco­brir o mapea­mento ou o processo é outro?
     
    Abraço

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org ser­gio­prado

      Olá Renato,

      O processo é outro. Nor­mal­mente, tudo que é depen­dente de arquite­tura é definido em “/arch/”. Dá uma olhada na minha resposta para o Daniel.

      Um abraço!

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • http://www.urano.com.br Adri­ano

    Olá Ser­gio ! 
     Você tem a ver­são eletrônica do livro do Sreekr­ish­nan Venkateswaran ?
    Grande abraço !

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org ser­gio­prado

      Olá Adri­ano!

      Tenho ape­nas a ver­são impressa. Mas vale a pena comprar!

      Abraços!

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • Car­los

    Parabens, Sér­gio! Uma ótima matéria para se dis­cu­tir. Seu blog está ótimo!

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
  • Thi­ago

    Ola Ser­gio,
    Como faco para fazer um pro­grama para apa­gar e acen­der leds?
    Obri­gado pelo artigo mais uma vez

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org ser­gio­prado

      Olá Thi­ago,

      Basta escr­ever um pro­grama para abrir por exem­plo “/dev/led1” e escr­ever “1” para acen­der ou “0” para apagar.

      Um abraço!

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • Thi­ago

    Real­mente e muito sim­ples, fun­cio­nou per­feita­mente.
    muito obri­gado!
     

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
  • Thi­ago

    Ser­gio,


    estou inter­es­sado em fazer dri­vers para out­ros per­iféri­cos. Porém nao con­segui desco­brir como vc fez para desco­brir que o endereço vir­tual da porta GPB é o 0xFB000010. Sim, eu fui no arquivo ¨arch/arm/mach-s3c2410/include/mach/regs-gpio.h¨ como indi­cado e mesmo assim nao encon­trei esta infor­ma­cao. Pode me dizer mais em detal­hes como vc con­seguiu isso? assim que tiver os dri­vers disponi­bi­lizo aqui para todos.

    obri­gado.

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org ser­gio­prado

      Olá Thi­ago!

      Eu desco­bri seguindo os defines no arquivo “arch/arm/mach-s3c2410/include/mach/regs-gpio.h”. De qual­quer forma, você não pre­cisa saber o valor. Pode usar dire­ta­mente o define. Por exem­plo, se quiser aces­sar aces­sar o reg­istrador de con­t­role do GPB, pode usar dire­ta­mente “S3C2410_GPBCON”.

      Um abraço.

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • Marcelo San­tos

    Como posso deixar esse código para rodar no ARM, com o GPIO157.
    Ape­nas para um LED?

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org Ser­gio Prado

      Olá Marcelo,

      Você pre­cisa alterar as definições de GPB_BASE, GPBCON e GPB_BASE da sua placa.

      Um abraço.

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • Marcelo San­tos

    Oi Ser­gio não quero ser igno­rante mas como fun­ciona essa nome­clatura  GPB_BASE em hexa­de­cial? no caso seria o endereço físico do GPIO?

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
  • http://www.sergioprado.org Ser­gio Prado

    Olá Marcelo,

    Sim, GPB_BASE é o endereço base de porta de I/O. Mas é a rep­re­sen­tação do endereço vir­tual. É uma infor­mação depen­dente de arquite­tura. No caso do kit mini2440, as definições encontram-se em “arch/arm/mach-s3c2410/include/mach/regs-gpio.h”.

    Então você vai ter que desco­brir qual o include no ker­nel que tem as definições dos GPIOs da sua placa (em arch/arm) e usá-lo.

    Um abraço.

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
  • Isa­ias Machado da Rosa

    Prezado Ser­gio, venho te pedir uma sug­estão para escr­ever e ler cor­re­ta­mente o dri­ver que estou desen­vol­vendo para o tra­balho da escola, segue abaixo o codigo.
    grato pela atenção
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <asm/uaccess.h>       
    #include <linux/cdev.h>

    #define SUCCESS 0 //constante de retorno
    #define DEVICE_NAME “converte_maiusculo” //nome do dri­ver que vai ser colo­cado no /dev

    //Variáveis globais são declar­adas como estáti­cas, dessa forma serão globais den­tro do arquivo.

    sta­tic int Major;  // numero do dri­ver
     
    sta­tic int Device_Open = 0;

    struct cdev paga;

    sta­tic dev_t numero_driver;

    int length = 1;
     
    int init_module(void); //funcao que e exe­cu­tada ao inserir o mod­ulo
     
    void cleanup_module(void);//funcao que e exe­cu­tada ao remover o modulo

    sta­tic int device_open(struct inode *, struct file *);// abre o driver

    sta­tic int device_release(struct inode *, struct file *); // fecha o driver

    sta­tic ssize_t device_read(struct file *, char *, size_t, loff_t *); // le o driver

    sta­tic ssize_t device_write(struct file *, const char *, size_t, loff_t *); // escreve no dri­ver
     
    //define qual fun­cao vai realizar qual ativi­dade
    sta­tic struct file_operations fops = {
            .read = device_read,
            .write = device_write,
            .open = device_open,
            .release = device_release,
            .owner = THIS_MODULE
    };
    /*
    sta­tic struct maius­culo    {
        char op[length];
        int tamanho;
     };*/
     
    //Esta fun­cao é chamada quando o mod­ulo e car­regado
    int init_module(void)    {
       
        Major = register_chrdev(0, DEVICE_NAME, &fops); //cria o dis­pos­i­tivo
     
        proc_create(DEVICE_NAME, 0, NULL, &fops); //cria um arquivo de debug em no dire­to­rio proc
     
        if (Major < 0) { //verifica a ocor­ren­cia de algum erro na cri­a­cao do dis­pos­i­tivo
            printk(KERN_ALERT “O reg­istro do converte_maiusculo fal­hou com o numero: %d\n”, Major);
            return Major;
        }
     
        //imprime men­sagens de uti­liza­cao do dis­pos­i­tivo
        printk(KERN_INFO “exe­cute o mknod com o nome %s e o major num­ber %d\n”, DEVICE_NAME, Major);

        cdev_init(&paga, &fops);
           
        paga.owner = THIS_MODULE;
      
        numero_driver = Major;
       
        cdev_add(&paga, numero_driver, 1);
     
        return SUCCESS;
    }
     
    //Esta fun­cao e chamada quando o mod­ulo e removido
     void cleanup_module(void)    {

        remove_proc_entry(DEVICE_NAME, NULL); //remove o arquivo de debug do dire­to­rio proc
     
        unregister_chrdev(Major, DEVICE_NAME); //remove o dis­pos­i­tivo
    }
     
    // chamada de aber­tura do dis­pos­i­tivo
    sta­tic int device_open(struct inode *inode, struct file *file)    {
       
        if (Device_Open) //verifica se ja tem alguem aces­sando o dis­pos­i­tivo
            return –EBUSY;//retorna antes de com­ple­tar, ape­nas um processo por vez pode uti­lizar o dri­ver
       
        Device_Open++; //bloqueia o acesso ao dis­pos­i­tivo por out­ros proces­sos
       
        try_module_get(THIS_MODULE);
     
        return SUCCESS;
     
    }
     
    //Chamada quando um processo fecha o arquivo.
     sta­tic int device_release(struct inode *inode, struct file *file)    {
       
        Device_Open–;//libera o acesso ao dis­pos­i­tivo       
       
        module_put(THIS_MODULE);
     
        return SUCCESS;
    }
     
    //Chamada quando um processo tenta ler o dri­ver
    sta­tic ssize_t device_read(struct file *file, char *buf, size_t count, loff_t *ppos)    {
        int tamanho;
        char *op = file->private_data;
        tamanho= sizeof(op);
       
        if(copy_to_user(buf, op ‚tamanho))
            return –EIO;

        return count;
    }

     //Chamada quando um processo tenta escr­ever no dri­ver
     sta­tic ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off)    {
          
        int i;
        char letra;
        char istring[len];

        if(copy_from_user(istring, buff, len))
            return –EFAULT;   
          
        for(i=0; i<len; i++){
           
            letra = istring[i];
           
            if(letra >= ‘a’ && letra <= ‘z’)     
                istring[i] = ‘A’ + letra — ‘a’;
               
            if(letra == –61 && istring[i+1] > –97){
                i++;
                letra = istring[i];
                istring[i] = letra — 32;
            }
        }
        filp->private_data = istring;
        //printk(KERN_INFO “%s\n”, istring);   
       
        return len;
    }

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org Ser­gio Prado

      Olá Isaías,

      Qual o problema/duvida?

      Um abraço.

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • EK

    Olá Ser­gio,
     
    Pode­rias enviar um exem­plo de como ficaria um mini-driver que con­trola os LEDs(Num lock, Caps lock, Scroll lock) de um teclado.
     
    Estou ten­tando criar meu primeiro dri­ver no linux, porém ainda estou meio perdido.

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)
    • http://www.sergioprado.org Ser­gio Prado

      Olá Elton,

      Infe­liz­mente estou sem tempo nos próx­i­mos dias para escr­ever este “mini-driver”. Mas gostei da idéia, e quem sabe esta imple­men­tação possa sair em um próx­imo artigo…:)

      Um abraço.

      VA:F [1.9.17_1161]
      Rating: 0.0/5 (0 votes cast)
  • EK

    Tá certo, vou aguardar o artigo. :)

    VA:F [1.9.17_1161]
    Rating: 0.0/5 (0 votes cast)