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.

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay