Atributos de classes
Tags: linux, lkm, linuxkernelmodule, drivers
Published: 2023-09-15 13:13:53

**Introdução**
Nesse terceiro texto da saga "introdução ao desenvolvimento de drivers para kernel linux" vou falar sobre atributos de classes. Os atributos de classes são arquivos criados no `/sys/class/classe_do_driver` que serão usados como forma de interagir com o SO.


**O que são atributos de classes**
Os atributos de classe, como mencionado na introdução são uma forma de comunicação do SO com o driver. Quando estamos desenvolvendo um driver para algum dispositivo podemos precisar de algumas informações que o kernel pode nos fornecer. 
A comunicação entre driver x Kernel é feita a partir de um arquivo que fica no `/sys/class/classe_do_driver`, e o nome do arquivo é dado por nós mesmo na hora de declarar o atributo. Então, como no texto anterior criamos uma classe chamada 7segment o diretorio da classe fica declarado em `/sys/class/7segment`. Como expliquei no texto anterior, essa classe `7segment` vamos usar futuramente para exibir um valor em um circuito simples com um display de 7 segmentos. Então, se precisarmos exibir algum número no display, podemos pegar o numero que esta declarado no atributo de classe da classe `7segment`. Então, criaremos um atributo de classe para a classe `7segment` e leremos esse atributo com o número para exibir no display. 
Gostaria de salientar, que nesse texto somente faleremos e implementaremos os códigos relacionados ao atributo de classe, e a implementação da comunicação com o display ficará para os próximos textos. 
Ficará mais claro com a implementação prática do código. 

Caso tenha caído de paraquedas nesse texto, aqui esta o [link](https://github.com/GuilhermeGiacomoSimoes/introducao_dev_driver_linux) com os códigos e links dessa serie.


**Mãos no código**
Para começar vamos criar os arquivos básicos, um `attribute.c` com a classe ja criada, assim como ensinamos no texto anterior, um `attribute.h` e um Makefile:

attribute.c:
```C
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>

#include "attribute.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)
{
	device_class = (struct class *) kzalloc(sizeof(struct class), GFP_ATOMIC);
	if(!device_class) 
	  pr_err("ERRO NA ALOCACAO DA CLASSE");

	device_class->name = "7segment";
	class_register(device_class);

	pr_info("class registrada");

	return 0;
}

static void __exit class_exit(void)
{
	class_unregister(device_class);
	class_destroy(device_class);
	pr_info("Modulo removido");

}

module_init(class_init);
module_exit(class_exit);
```

attribute.h
```C
#ifndef __MY_CLASS_H__
#define __MY_CLASS_H__

static struct class *device_class = NULL;

#endif 
```

Makefile
```Makefile
obj-m += attribute.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
```

**Como declarar um atributo de classe**
Agora vamos começar a criar nosso atributo de classe. Para isso, no attribute.h vamos criar uma variável do tipo `class_attribute`, que que fica declarado dentro do header `device.h` no path `include/linux/device.h` da árvore de código do kernel.

atrribute.h
```C
//...
static struct class_attribute *attr = NULL;
//...
```
E após declara-lo, vamos alocar memória para ele na nossa função de inicialização do módulo após ter registrado nossa classe:
```C
//...
static int __init class_init(void)
{
    //...
    //declarao e registro da classe

    attr = (struct class_attribute *) kzalloc(sizeof(struct class_attribute), GFP_ATOMIC);
    attr->show = show_value;
    attr->store = store_value;
    attr->attr.name = "value";
    attr->attr.mode = 0777;
    class_create_file(device_class, attr);

    //...
```

Agora vamos a alguns esclarecimentos. O atributo nome é o mais óbvio de todos, ele espera receber um nome de atributo, aqui chamamos apenas de "value". Como mencionei, quando criamos o atributo ele cria um arquivo no diretorio `/sys/class/classe_do_device`, como chamamos nosso atributo de "value" e nossa classe de 7segment então ele ficará no path `/sys/class/7segment/value`. Esse arquivo conterá o valor do atributo. 
O atributo mode, recebe as permissoes do arquivo, aqui demos a permissao mais aberta de todas (777 - leitura e escrita pra qualquer ususario) porque isso é apenas um tutorial extremamente introdutório, é importante que você entenda sobre as permissões e coloque uma adequada quando fizer isso em produção. 
Temos outros dois atributos: show e value. Eles esperam duas funções uma que será chamada quando o arquivo for aberto (show) e uma que será chamada quando o arquivo for fechado (store). Então precisamos declarar a assinatura dessas funções no nosso header e também declarar uma variável que vai receber o valor que esta no atributo.

attribute.h
```C
//...
static ssize_t show_value(const struct class *class, 
		const struct class_attribute *attr, char* buf);

static ssize_t store_value(const struct class *class, 
		const struct class_attribute *attr, const char* buf, size_t count);

volatile int value_display;
```

E agora no attribute.c vamos implementa-las
attribute.c
```C
static ssize_t show_value(const struct class *class, 
		const struct class_attribute *attr, char* buf) 
{
	pr_info("valor do display %d - LEITURA", value_display);
	return sprintf(buf, "%d", value_display);
}

static ssize_t store_value(const struct class *class, 
		const struct class_attribute *attr, const char* buf, size_t count) 
{
	sscanf(buf, "%d", &value_display);
	pr_info("valor do display %d - ESCRITA", value_display);
        return count;
}
```

Perfeito, agora quando abrirmos o arquivo `sys/class/7segment/value` a função `show_value` vai executar e poderemos ler o valor que esta nele, e quando editarmos esse arquivo a função `store_value()` vai ser executada e salvar o nosso novo valor em `sys/class/7segment/value`.


**Compilando e inserindo o modulo no kernel**
Vamos compilar e inserir o módulo no kernel.
```bash
make
```

```bash
sudo insmod attribute.ko
```

E vamos conferir os logs `sudo dmesg`
E obtemos a seguinte saída 
```bash
[  958.089903] attribute: loading out-of-tree module taints kernel.
```
Incluímos nosso código no kernel.

Agora, vamos dar um `ls` em `sys/class/7segment`.
E veja só que coisa mais bonita:
![listagem do diretorio sys/class/7segment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lunzvnddv4x11a1xakjf.png)

Bom, agora vamos manipular o valor dentro do arquivo `value` e verificar os logs do Kernel.
Vou abrir o arquivo e colocar la dentro o valor 8 e vou consultar os logs `sudo dmesg`

![Saida dos logs do kernel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qmmfqvujwxbrxkmmltn.png)

Pronto, conseguimos gravar um novo atributo e ler ele do arquivo de atributo de classe.

**Revisão**
- Vimos o que são atributos de classe
- Aprendemos a criar um atributo de classe
- Aprendemos a ler e gravar um novo atributo de classe