[Linux] O primeiro driver, olá mundo! Comece o passeio de carro

Índice

Prefácio: 

 1. Fundo

2. Processo de escrita do driver  

1. Como o arquivo aberto pelo APP é representado no kernel?

2. O processo de escrever o driver

Três, olá motorista combate real

1.hello_drive.c

2.hello_drive_test.c

3. Teste final:

a. Primeiro compile o kernel (se não estiver compilado)

b. Configurar a cadeia de ferramentas de compilação cruzada

c. Escrever makefile

d. Teste na máquina

e. Resultado final:


Prefácio: 

Link clássico: pensando e praticando com perguntas.

(1) Qual é o driver mais simples?

(2) Como escrever o driver?

  • Como o arquivo aberto pelo APP é representado no kernel?
  • Compreender o processo de escrever um programa
    • Como chamar a função aberta/leitura/gravação autoescrita?
    • Como dizer ao kernel que há um novo driver? Como o kernel encontra drivers?
    • Como instalar e desinstalar o driver?

(3) Olá motorista combate real

Se ajudar, por favor me apoie muito, isso vai me dar mais motivação para criar!

 1. Fundo

Olhando para o artigo anterior, quando nosso APP chama a função open/read/write na glibc, ele vai do estado do usuário para o estado do kernel, chama o sys_open, sys_read e outras funções correspondentes para acessar os arquivos no kernel, abra o nó do dispositivo correspondente, localize e inicie o driver correspondente. Entre eles, como transferir do modo de usuário para o modo kernel e outros conhecimentos relacionados, consulte o artigo:

https://blog.csdn.net/weixin_42373086/article/details/129913881?spm=1001.2014.3001.5501

Qual é o driver mais simples?

Aqui está a maneira mais simples de chamar o driver. O driver também fornece suas próprias funções, como drv_open e drv_read. Um driver corresponde a um dispositivo.

2. Processo de escrita do driver  

Com base no exposto, para concluir um programa de driver simples e básico, precisamos implementar as funções abrir/ler/gravar por nós mesmos, mas ainda existem alguns problemas específicos na implementação completa de um programa de driver.

Questões específicas:

  • Como o arquivo aberto pelo APP é representado no kernel?
  • Entenda o processo de escrever um programa:
    • Como chamar a função aberta/leitura/gravação autoescrita?
    • Como dizer ao kernel que há um novo driver? Como o kernel encontra drivers?
    • Como instalar e desinstalar o driver?

1. Como o arquivo aberto pelo APP é representado no kernel?

Quando o APP abre um arquivo, ele pode obter um número inteiro (manipulação de arquivo) Para cada identificador de arquivo do APP, haverá um "arquivo struct" correspondente a ele no kernel. Ao abrir um nó de dispositivo de caractere, também há um arquivo struct correspondente no kernel.

Ao usar open para abrir um arquivo, os parâmetros como sinalizadores e modo passados ​​serão registrados na estrutura de arquivo struct correspondente no kernel (f_flags, f_mode):

Ao ler e gravar arquivos, o endereço de deslocamento atual do arquivo também será armazenado no membro f_pos da estrutura do arquivo struct.

Ao abrir um nó de dispositivo de caractere, também há um arquivo struct correspondente no kernel. Observação: a estrutura struct file_operations *f_op no membro é fornecida pelo driver.

2. O processo de escrever o driver

    • ①Determine o número do dispositivo principal ou deixe o kernel alocar
    • ②Defina sua própria estrutura de operações_de_arquivo---Nota: Existem funções de abertura/leitura/gravação do driver nela
    • ③ Implemente as funções correspondentes, como drv_open/drv_read/drv_write, e preencha a estrutura file_operations
    • ④Diga a estrutura file_operations para o kernel: register_chrdev
    • ⑤ Quem registrará o motorista? Deve haver uma função de entrada : quando o driver for instalado, esta função de entrada será chamada
    • ⑥Se houver uma função de entrada, deve haver uma função de saída : quando o driver é desinstalado, a função de saída chama unregister_chrdev
    • ⑦Outras melhorias: fornecer informações do dispositivo e criar nós de dispositivo automaticamente: class_create, device_create

Três, olá motorista combate real

Um programa de driver é inicialmente escrito aqui, e o corpo principal aqui é o programa de driver hello_drive.c e o programa de teste hello_drive_test.c.

Observação: A função copy_from_user/copy_to_user deve ser usada para os dados entre o programa do driver e o programa aplicativo.

1.hello_drive.c

/*
	hello_drive.c
*/

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

/*第一步:确定主设备号,也可以让内核分配*/
static int major = 0;

//用于保存应用程序下发的数据,定义一个buffer
static char kernel_buffer[1024];
static struct class* hello_class;
#define MIN(a, b) (a < b ? a : b)


/*第三步:实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体*/
static ssize_t hello_drv_read (struct file * file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_to_user(buf, kernel_buffer, MIN(1024, size));
	return MIN(1024, size);
}
static ssize_t hello_drv_write (struct file * file,const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(kernel_buffer, buf, MIN(1024, size));
	return MIN(1024, size);
}
static int hello_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}
static int hello_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


/*第二步:定义自己的 file_operations 结构体---注:里面存有驱动的open/read/write函数*/
static struct file_operations hello_drv = {
	.owner 		= THIS_MODULE,
	.open  		= hello_drv_open,
	.read  		= hello_drv_read,
	.write 		= hello_drv_write,
	.release 	= hello_drv_close,
};

/*第四步:把 file_operations 结构体告诉内核:register_chrdev*/
/*第五步:定义一个入口函数, 调用register_chrdev*/
static int __init hello_init(void)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "hello", &hello_drv);

	//创建一个类,提供设备信息
	hello_class = class_create(THIS_MODULE, "hello_class");
	err = PTR_ERR(hello_class);
	if (IS_ERR(hello_class)){
		unregister_chrdev(major, "hello");
		return -1;
	}

	device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello");
	return 0;
}


/*第六步:定义一个出口函数,出口函数调用unregister_chrdev*/
static void __exit hello_exit(void)
{
	device_destroy(hello_class, MKDEV(major, 0));
	class_destroy(hello_class);
	major = unregister_chrdev(0, "hello");
}

/*第七步:其他完善:提供设备信息,自动创建设备节点:class_create、device_create*/

//把函数修饰成入口函数和出口函数
module_init(hello_init);
module_exit(hello_exit);

//内核遵循GPL协议,所以我们也要遵循GPL协议,才能够使用内核里的一些函数
MODULE_LICENSE("GPL");

2.hello_drive_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/*
 * ./hello_drv_test -w abc
 * ./hello_drv_test -r
 */
int main(int argc, char **argv)
{
	int fd;
	char buf[1024];
	int len;
	
	/* 1. 判断参数 */
	if (argc < 2) 
	{
		printf("Usage: %s -w <string>\n", argv[0]);
		printf("       %s -r\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open("/dev/hello", O_RDWR);
	if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return -1;
	}

	/* 3. 写文件或读文件 */
	if ((0 == strcmp(argv[1], "-w")) && (argc == 3))
	{
		len = strlen(argv[2]) + 1;
		len = len < 1024 ? len : 1024;
		write(fd, argv[2], len);
	}
	else
	{
		len = read(fd, buf, 1024);		
		buf[1023] = '\0';
		printf("APP read : %s\n", buf);
	}
	
	close(fd);
	
	return 0;
}

3. Teste final:

a. Primeiro compile o kernel (se não estiver compilado)

Consulte este artigo https://blog.csdn.net/weixin_42373086/article/details/129796348?spm=1001.2014.3001.5501

b. Configurar a cadeia de ferramentas de compilação cruzada

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin

c. Escrever makefile

Nota: KERN_DIR deve corresponder ao kernel na placa de desenvolvimento.

KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88

all:
        make -C $(KERN_DIR) M=`pwd` modules
        $(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c

clean:
        make -C $(KERN_DIR) M=`pwd` modules clean
        rm -rf modules.order
        rm -f hello_drv_test

obj-m   += hello_drv.o

 Em seguida, compila e copia o arquivo .ko e hello_drive_test para a pasta montada do nfs.

cp *.ko hello_drv_test ~/nfs_rootfs/

d. Teste na máquina

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
//复制到开发板上
cp /mnt/hello_drive.ko ./
cp /mnt/hello_drive_test ./
//安装驱动模块
insmod hello_drive.ko

//查询是否有我们的hello程序
cat /proc/devices
lsmod

//查询是否有我们的设备节点
ls /dev/hello -l
//写入
./hello_drive_test -w hello_world!
//读取
./hello_drive_test -r 

e. Resultado final:

 

Acho que você gosta

Origin blog.csdn.net/weixin_42373086/article/details/130545341
Recomendado
Clasificación