DEV Community

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

Posted on

Atributos de classes

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 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:

#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);
Enter fullscreen mode Exit fullscreen mode

attribute.h

#ifndef __MY_CLASS_H__
#define __MY_CLASS_H__

static struct class *device_class = NULL;

#endif 
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

//...
static struct class_attribute *attr = NULL;
//...
Enter fullscreen mode Exit fullscreen mode

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:

//...
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);

    //...
Enter fullscreen mode Exit fullscreen mode

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

//...
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;
Enter fullscreen mode Exit fullscreen mode

E agora no attribute.c vamos implementa-las
attribute.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;
}
Enter fullscreen mode Exit fullscreen mode

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.

make
Enter fullscreen mode Exit fullscreen mode
sudo insmod attribute.ko
Enter fullscreen mode Exit fullscreen mode

E vamos conferir os logs sudo dmesg
E obtemos a seguinte saída

[  958.089903] attribute: loading out-of-tree module taints kernel.
Enter fullscreen mode Exit fullscreen mode

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

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

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

Top comments (0)