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
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);
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
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
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"
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");
Após a nossa alocação de classe vamos preencher somente um atributo dessa classe: O seu nome.
device_class->name = "7segment";
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);
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);
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;
}
E no exit:
static void __exit class_exit(void)
{
//...
pr_info("Modulo removido");
}
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
Além disso, se consultarmos o diretorio /sys/class
temos o diretório da classe 7segment que criamos.
Quando removermos o modulo: sudo rmmod my_class
. O diretório da classe também é removido.
Revisão
- Aprendemos o que é uma classe de dispositivo.
- Como criar uma nova classe de dispositivo.
- Vimos também uma BREVÍSSIMA introdução sobre a alocação de memória no kernel.
Top comments (0)