Desenvolvimento de Drivers Linux para Dispositivos de Carácter

Conceito de Drivers em Sistemas Embacrados

Em sistemas embarcados, um driver atua como uma ponte entre aplicações de espaço de usuário e hardwrae subjacente. Como aplicações executam em modo usuário, elas não possuem acesso direto ao hardware. Através de chamadas de sistema como open, read e write, a execução transita para o modo kernel, permitindo interação com dispositivos por meio de nós correspondentes e funções implementadas no driver.

Estrutura Básica de um Driver de Dispositivo de Carácter

Os passos fundamentais para desenvolver um driver são:

  • Definir um número de dispositivo principal
  • Implementar a estrutura file_operations com ponteiros para funções
  • Codificar as funções de operação do dispositivo
  • Criar funções de inicialização e remoção do módulo
  • Registrar a estrutura no kernel e criar entradas no sistema de aqruivos
  • Utilizar macros para declarar funções de entrada e saída
  • Incluir a licença GPL para compatibilidade com o kernel

Implementação do Driver

O desenvolvimento requer três arquivos principais:

  • Arquivo do driver: modulo_driver.c
  • Aplicação de teste: teste_driver.c
  • Makefile para compilação

O código do driver inclui:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>

static int major_num = 0;
static char buffer_kernel[1024];
static struct class *classe_dispositivo;

#define LIMITE_TAMANHO(a, b) ((a) < (b) ? (a) : (b))

static int abrir_dispositivo(struct inode *inode, struct file *arquivo) {
    printk(KERN_INFO "Dispositivo aberto\n");
    return 0;
}

static ssize_t ler_dispositivo(struct file *arquivo, char __user *buffer, size_t tamanho, loff_t *offset) {
    int bytes_copiados;
    bytes_copiados = copy_to_user(buffer, buffer_kernel, LIMITE_TAMANHO(1024, tamanho));
    return bytes_copiados;
}

static ssize_t escrever_dispositivo(struct file *arquivo, const char __user *buffer, size_t tamanho, loff_t *offset) {
    int bytes_copiados;
    bytes_copiados = copy_from_user(buffer_kernel, buffer, LIMITE_TAMANHO(1024, tamanho));
    return bytes_copiados;
}

static int liberar_dispositivo(struct inode *inode, struct file *arquivo) {
    printk(KERN_INFO "Dispositivo liberado\n");
    return 0;
}

static struct file_operations operacoes_arquivo = {
    .owner = THIS_MODULE,
    .open = abrir_dispositivo,
    .read = ler_dispositivo,
    .write = escrever_dispositivo,
    .release = liberar_dispositivo,
};

static int __init iniciar_modulo(void) {
    int resultado;
    major_num = register_chrdev(0, "modulo_exemplo", &operacoes_arquivo);
    
    classe_dispositivo = class_create(THIS_MODULE, "classe_modulo");
    if (IS_ERR(classe_dispositivo)) {
        unregister_chrdev(major_num, "modulo_exemplo");
        return PTR_ERR(classe_dispositivo);
    }
    
    device_create(classe_dispositivo, NULL, MKDEV(major_num, 0), NULL, "modulo_exemplo");
    return 0;
}

static void __exit remover_modulo(void) {
    device_destroy(classe_dispositivo, MKDEV(major_num, 0));
    class_destroy(classe_dispositivo);
    unregister_chrdev(major_num, "modulo_exemplo");
}

module_init(iniciar_modulo);
module_exit(remover_modulo);
MODULE_LICENSE("GPL");

Aplicação de Exemplo para Teste

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv) {
    int descritor_arquivo;
    char buffer[1024];
    int bytes_lidos;

    if (argc < 2) {
        printf("Uso: %s -w <texto>\n", argv[0]);
        printf("       %s -r\n", argv[0]);
        return -1;
    }

    descritor_arquivo = open("/dev/modulo_exemplo", O_RDWR);
    if (descritor_arquivo < 0) {
        perror("Falha ao abrir dispositivo");
        return -1;
    }

    if (strcmp(argv[1], "-w") == 0 && argc == 3) {
        write(descritor_arquivo, argv[2], strlen(argv[2]) + 1);
    } else {
        bytes_lidos = read(descritor_arquivo, buffer, sizeof(buffer) - 1);
        buffer[bytes_lidos] = '\0';
        printf("Dados lidos: %s\n", buffer);
    }

    close(descritor_arquivo);
    return 0;
}

Makefile para Compilação Cruzada

DIR_KERNEL = /caminho/para/kernel

all:
	make -C $(DIR_KERNEL) M=$(shell pwd) modules
	$(CROSS_COMPILE)gcc -o teste_driver teste_driver.c

clean:
	make -C $(DIR_KERNEL) M=$(shell pwd) clean
	rm -f teste_driver

obj-m += modulo_driver.o

Instalação e Teste do Driver

Após a compilação, transfira os arquivos modulo_driver.ko e teste_driver para o dispositivo alvo. Execute os comandos:

insmod modulo_driver.ko
./teste_driver -w "Texto de exemplo"
./teste_driver -r
rmmod modulo_driver

Tags: Linux driver kernel char_device file_operations

Publicado em 9-16 17:57