欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux驱动开发(二)注册字符设备

程序员文章站 2024-02-01 15:04:46
...

对于字符设备驱动程序,最核心的就是file_operations结构,这个架构对应提供给虚拟文件系统(VFS)的文件结构,它的每一个成员函数一般都对应一个系统调用。用户进程利用系统调用对设备文件进行诸如读和写等操作时,系统调用通过设备文件的煮设备号找到响应的设备驱动程序。file_operations结构体定义如下:

Linux驱动开发(二)注册字符设备

Linux驱动开发(二)注册字符设备

注册字符设备使用register_chrdev:

register_chrdev函数原型为:

int register_chrdev(unsigned int major,const *name,struct file_operation *fops);

参数major为主设备号,可以在写驱动的时候指定,当一般设置为0。major为0时,则主设备号由系统动态分配。

注销字符设备使用unregister_chrdev:

unregister_chrdev函数原型为:

int unregister_chrdev(unsigned int major,const char *name);

具体例子:

字符驱动

simple_chrdev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <asm/errno.h>

#define simple_MAJOR 0
static unsigned char simple_inc = 0;
static unsigned char demoBuffer[256];


int simple_open(struct inode *inode,struct file *filp)
{
	if(simple_inc>0)return -ERESTARTSYS;
	simple_inc++;
	return 0;
}

int simple_release(struct inode *inode,struct file *filp)
{
	simple_inc--;
	return 0;
}

ssize_t simple_read(struct file *filp,char __user *buf,size_t count,loff_t *fpos)
{
	if(copy_to_user(buf,demoBuffer,count)) //数据内核空间都用户空间的拷贝
	{
		count = -EFAULT;
	}
	return count;
}

ssize_t simple_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
{
	if(copy_from_user(demoBuffer,buf,count)) //数据从用户空间到内核空间的拷贝
	{
		count = -EFAULT;
	}
	return count;
}

struct file_operations simple_fops = {

	.owner   = THIS_MODULE,
	.read    = simple_read,
	.write   = simple_write,
	.open    = simple_open,
	.release = simple_release,
};


void simple_cleanup_module(void)
{
	unregister_chrdev(simple_MAJOR, "simple");
	printk("simple_cleanup_module!\n");
}

int simple_init_module(void)
{
	int ret;
	ret = register_chrdev(simple_MAJOR,"simple",&simple_fops);
	if(ret < 0)
	{
		printk("Unable to register character device %d\n",simple_MAJOR);
		return ret;
	}
	return 0;
}

module_init(simple_init_module);
module_exit(simple_cleanup_module);
MODULE_LICENSE("GPL");//没加这句可能会报错module license 'unspecified' taints kernel

Makefile:

obj-m := simple_chrdev.o

KDIR := /home/vinkim/OrangePiH6/kernel

PWD := $(shell pwd)

default:
	make ARCH=arm64 CROSS_COMPILE=/opt/toolchain/gcc-linaro-aarch/bin/aarch64-linux-gnu- -C $(KDIR) M=$(PWD) modules


clean:
	rm -rf *.o *.mod.* *.ko *.symvers *.order

KDIR:内核所在目录

ARCH为芯片架构,我这里为arm64架构

CROSS_COMPILE为交叉编译工具链

编译成功后生成simple_chrdev.ko文件

测试程序:

main.c

#include <linux/fs.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>


void main(void)
{
	int fd;
	int i;
	char data[256];
	int retval;

	fd = open("/dev/vinkim",O_RDWR);

	if(fd == -1)
	{
		perror("error open\n");
		exit(-1);
	}
	printf("open /dev/vinkim successfully.\n");
	retval = write(fd,"vinkim",6);

	if(retval == -1)
	{
		perror("write error!\n");
		exit(-1);
	}

	retval = read(fd,data,6);
	if(retval == -1)
	{
		perror("read error!\n");
		exit(-1);
	}
	data[retval] = 0;
	printf("read successfully:%s\n",data);
	close(fd);
}

编译生成main.o文件

将simple_chrdev.ko和main.o拷贝到linux开发板上

1.加载simple_chrdev.ko模块:

insmod simple_chrdev.ko

2.创建字符设备文件

创建字符设备文件之前需要先查询字符设备文件的主设备号,系统是通过主设备号将字符设备文件和字符驱动进行关联的。

cat /proc/devices

我们注册字符设备的名称为"simple"

Linux驱动开发(二)注册字符设备

我们可以看到simple所对应的主设备号为240

这样我们就可以创建我们的字符设备文件了:

mknod /dev/vimk c 240 0

vimk为创建的文件名,这个可以随便名

c表示穿件的我字符设备文件

240就是我们上一步查询到的主设备号

0位从设备号

3.运行测试程序

./main.o

Linux驱动开发(二)注册字符设备