DEV Community

Guilherme Giácomo Simões
Guilherme Giácomo Simões

Posted on • Edited on

Classe de dispositivos

Nesse segundo texto sobre a introdução ao desenvolvimento de drivers de dispositivos pro kernel linux, vamos falar sobre classes de dispositivos.

Caso tenha caído de paraquedas nesse texto, aqui esta o link do github com o compilado dos códigos e links dessa serie.

O que são classes de dispositivos?
Classes de dispositivos é uma forma de classificar a familia do dispositivo do qual voce esta escrevendo o driver e tambem uma forma de facilitar uma comunicação do kernel com o driver em questão.

Os dispositivos são enumerados nas classes e cada vez que um novo driver é atribuído a classe ele incrementa um atributo de classe chamado devnum, e atribui esse devnum ao dispositivo. O devnum nunca é decrementado, por tanto se você remover o dispositivo e inserir novamente ele receberá outro devnum.

Após inserirmos o módulo de driver no kernel, ele criará um diretório com o nome da sua classe no caminho /sys/class .
Se quisermos ver as classes de dispositivos registradas no nosso kernel, podemos listar os diretorios em /sys/class

listagem das classes registradas no kernel

Podemos observar que temos classes de bluetooth, graphics, cpuid, etc.
Essas são as classes registradas no meu kernel, e podemos criar uma nova classe de dispositivo, ou usar alguma já existente como a classe de usb ou bluetooth.
Nesse texto, vamos criar nossa própria classe. Toda classe deve ficar declarada em um arquivo header para que seja usada pelos drivers, extensões e interfaces. Como os dispositivos estão vinculados aos drivers, eles devem ser adicionados a classe a qual pertencem.

Para criar uma classe de dispositivo e consequentemente um diretorio para essa classe no /sys/class , é preciso registrar essas informações em uma struct chamada class que fica declarada dentro do header include/linux/device.h. , portanto…

Mãos no codigo
Entao, sem mais enrolação, vamos criar um novo módulo chamado my_class.c, lembrando que todos os códigos completos estão no link do github citado acima.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_AUTHOR("SEU NOME <seu_email@email.com>");
MODULE_DESCRIPTION("Classes de dispositivos");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");

static int __init class_init(void)
{
  return 0;
}

static void __exit class_exit(void)
{}

module_init(class_init);
module_exit(class_exit);
Enter fullscreen mode Exit fullscreen mode

Assim como seu Makefile padrão:

obj-m += my_class.o

all: run

run:
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Enter fullscreen mode Exit fullscreen mode

Vamos declarar também um header my_class.h pra organizar nosso código e dentro do header vamos já criar nossa variável device_class.

#ifndef __MY_CLASS_H__
#define __MY_CLASS_H__

static struct class *device_class = NULL;

#endif
Enter fullscreen mode Exit fullscreen mode

Agora precisamos tanto importar o header device.h quanto o nosso header my_class.h dentro do my_class.c .

#include <linux/device.h>
#include "my_class.h"
Enter fullscreen mode Exit fullscreen mode

Para criarmos a nossa classe, precisamos agora alocar memória para a struct e preencher seus atributos.
Provavelmente você esta acostumado a usar o malloc() para alocação de memória em aplicações C em nível de usuário. Porém o malloc() faz parte da biblioteca stdlib.h e nós não temos acesso as bibliotecas no nível do kernel.
Porém, no kernel temos um header chamado slab.h que tem o poder de fazer alocações de memória, então, podemos importar ele no nosso my_class.c: #include <linux/slab.h>. E depois usar a função kzalloc() para alocar memória para a nossa classe.

Uma coisa a se frizar da função kzalloc() é que ela recebe dois parametros, ao contrario de malloc() que só recebe um: A quantidade de bytes a ser alocado.
O kzalloc() recebe a quantidade de bytes a ser alocado e mais uma flag, que diz respeito ao controle do comportamento dos alocadores.

Segundo a Documentação oficial:

Eles informam quais zonas de memória podem ser usadas, o quanto o alocador deve tentar encontrar memória livre, se a memória pode ser acessada pelo espaço do usuário, etc.

Caso queira mais informações sobre as flags de alocacao de memoria, pode acessar esse link.
No nosso caso, vamos utilizar a flag GFP_ATOMIC , que sinaliza ao alocador que a reserva de memória é extremamente justificada e o kernel terá problemas caso a alocação não seja feita. Porém para a maioria dos casos, GFP_KERNEL resolve.

Então alocaremos memória para a nossa classe dentro da nossa função de inicialização do modulo:

#include <linux/slab.h>

// ... 
static int __init class_init(void)
{
  device_class = (struct class *) kzalloc(sizeof(struct class), GFP_ATOMIC);
  if(!device_class) 
    pr_err("ERRO NA ALOCACAO DA CLASSE");
Enter fullscreen mode Exit fullscreen mode

Após a nossa alocação de classe vamos preencher somente um atributo dessa classe: O seu nome.

 device_class->name = "7segment";
Enter fullscreen mode Exit fullscreen mode

O atributo name é bem óbvio. Ele é somente o nome da classe do nosso dispositivo. Aqui declarei "7segment" porque nos próximos textos, usaremos esse modulo e construiremos um circuito simples em uma protoboard e controlaremos um display de 7 segmentos através do kernel.

Por fim, chamaremos uma função dentro da função de inicialização que registrará nossa classe e criará um diretório para a classe dentro de /sys/class com o nome que especificamos, nesse caso 7segment.

class_register(device_class);
Enter fullscreen mode Exit fullscreen mode

Agora, na função de __exit precisamos matar a classe de dispositivo que criamos, então vamos incrementar essa linha de código na função de __exit

 class_unregister(device_class);
 class_destroy(device_class);
Enter fullscreen mode Exit fullscreen mode

Somente para fins de visualização, vamos colocar dois pr_info() , um no__init e outro no __exit para printar algo no console

No init:

static int __init class_init(void)
{
  //...
  pr_info("class registrada");
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

E no exit:

static void __exit class_exit(void)
{
  //...
 pr_info("Modulo removido");
}
Enter fullscreen mode Exit fullscreen mode

Vamos compilar: make .
Vamos limpar o nosso log para melhor visualizar o resultado do nosso módulo sudo dmesg -C.
E por fim vamos inserir o módulo no kernel: sudo insmod my_class.ko .
Após isso a saída do nosso sudo dmesg :

[ 1107.594074] my_class: loading out-of-tree module taints kernel.
[ 1107.594093] my_class: class registrada
Enter fullscreen mode Exit fullscreen mode

Além disso, se consultarmos o diretorio /sys/class temos o diretório da classe 7segment que criamos.

saida do dmesg

Quando removermos o modulo: sudo rmmod my_class . O diretório da classe também é removido.

Revisão

  1. Aprendemos o que é uma classe de dispositivo.
  2. Como criar uma nova classe de dispositivo.
  3. Vimos também uma BREVÍSSIMA introdução sobre a alocação de memória no kernel.

Top comments (0)