Índice
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
a. Primeiro compile o kernel (se não estiver compilado)
b. Configurar a cadeia de ferramentas de compilação cruzada
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: