linux驱动 5.字符设备驱动程序

本文详细介绍字符设备驱动的编写过程,包括设备结构体定义、模块加载函数、file_operations成员函数及其实现示例。通过具体代码示例,展示了如何在Linux环境下创建、注册和使用字符设备。

在这里写一下字符设备驱动的编写。主要包含几部分,分别如下:

1.设备结构体

这个结构体中一般包含cdev,私有数据,以及锁等数据。

struct xxx_dev_t {
 struct cdev cdev;
 ...
 
} xxx_dev;

2.设备驱动模块加载函数

这个中一般包括,cdev的初始化,获取字符设备号,注册设备,创建device和class,初始话互斥变量。

static int _ _init xxx_init(void)
{
 ...
 cdev_init(&xxx_dev.cdev, &xxx_fops); /* 初始化 cdev */
 xxx_dev.cdev.owner = THIS_MODULE;
 /* 获取字符设备号 */
 if (xxx_major) {
 register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
 } else {
 alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
 }

 ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); /* 注册设备 */
 ...
 xxx_dev->class = class_create(THIS_MODULE, xxx_DEV_NAME);
 ...
 xxx_dev->device = device_create(xxx_dev->class, NULL,
			xxx_dev->devno, NULL, xxx_DEV_NAME);
.....
mutex_init(&xxx_dev->xxx_mutex);
}

3.字符设备驱动的file_operations结构体中的成员函数

file_operations结构体中的成员函数是字符设备驱动与内核虚拟文件系统的接口,是用户空间对Linux进行系统调用最终的落实者。大多数字符设备驱动会实现read()、write()和ioctl()函数,常见的字符设备驱动的这3个函数的形式如下代码所示。

 /* 读设备 */
 ssize_t xxx_read(struct file *filp, char __user *buf, size_t count,loff_t*f_pos)
 {
 ...
 copy_to_user(buf, ..., ...);
 ...
  }
 /* 写设备 */
 ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
 {
 ...
 copy_from_user(..., buf, ...);
 ...
 }
 /* ioctl 函数 */
 long xxx_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
 {
	 ...
	 switch (cmd) {
 		case XXX_CMD1:
 				...
 				break;
 		default:
			 	/* 不能支持的命令 */
	 			return - ENOTTY;
	 }
 	return 0;
 }

在字符设备驱动中,需要定义一个file_operations的实例,并将具体设备驱动的函数赋值给file_operations的成员,如下代码所示

struct file_operations xxx_fops = {
 .owner = THIS_MODULE,
 .read = xxx_read,
 .write = xxx_write,
 .unlocked_ioctl= xxx_ioctl,
  ...
};

字符设备驱动与字符设备以及字符设备驱动与用户空间访问该设备的程序之间的关系。如下图所示:
在这里插入图片描述
4 实例程序:

下面是一个简单的示例程序,实现一个简单的字符驱动程序:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <asm/ioctl.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#define CALCULATOR_DEV_NAME		"calculator"
#define CALCULATOR_SUM	 		1
#define VAR_SIZE 		        3*sizeof(unsigned long)

struct calculator_dev {
	struct cdev cdev;
	dev_t devno;
	struct class *class;
	struct device *device;
	struct mutex calculator_mutex;
};


struct calculator_dev *calculator_dev = NULL;

static int calculator_open(struct inode *inode, struct file *filp)
{
	filp->private_data = calculator_dev;

	return 0;
}

static int calculator_close(struct inode *inode, struct file *filp)
{
	return 0;
}


static long calculator_ioctl(struct file *filp, unsigned int cmd,
		unsigned  long arg)
{
	struct calculator_dev *dev = filp->private_data;
	unsigned long var[3];

	switch (cmd) {
		case CALCULATOR_SUM:
			mutex_lock(&dev->calculator_mutex);
			if (copy_from_user(&var,(void __user *)arg, VAR_SIZE)) {
				pr_err("copy_from_user fail:%d\n", __LINE__);
				return -EFAULT;
			}
                        var[2]=var[1]+var[0];
			printk("The sum is %ld\n",var[2]);
			if (copy_to_user((void __user *)arg,&var, VAR_SIZE)) {
				pr_err("copy_from_user fail:%d\n", __LINE__);
				return -EFAULT;
			}
			mutex_unlock(&dev->calculator_mutex);
			break;
		default:
			return -EINVAL;
	}

	return 0;
}

static struct file_operations calculator_fops = {
	.owner = THIS_MODULE,
	.open = calculator_open,
	.release = calculator_close,
	.compat_ioctl = calculator_ioctl,
	.unlocked_ioctl = calculator_ioctl,
};

static int __init calculator_init(void)
{
	int ret;

	if (calculator_dev == NULL) {
		calculator_dev = (struct calculator_dev *)kzalloc(sizeof(struct calculator_dev), GFP_KERNEL);
		if (calculator_dev == NULL) {
			pr_err("struct calculator_dev allocate fail\n");
			return -1;
		}
	}

	/* allocate device number */
	ret = alloc_chrdev_region(&calculator_dev->devno, 0, 1, CALCULATOR_DEV_NAME);
	if (ret < 0) {
		pr_err("alloc_chrdev_region fail:%d\n", ret);
		goto alloc_chrdev_err;
	}

	/* set char-device */
	cdev_init(&calculator_dev->cdev, &calculator_fops);
	calculator_dev->cdev.owner = THIS_MODULE;
	ret = cdev_add(&calculator_dev->cdev, calculator_dev->devno, 1);
	if (ret < 0) {
		pr_err("cdev_add fail:%d\n", ret);
		goto cdev_add_err;
	}
	/* create device */
	calculator_dev->class = class_create(THIS_MODULE, CALCULATOR_DEV_NAME);
	if (IS_ERR(calculator_dev->class)) {
		pr_err("class_create fail\n");
		goto class_create_err;
	}
	calculator_dev->device = device_create(calculator_dev->class, NULL,
			calculator_dev->devno, NULL, CALCULATOR_DEV_NAME);
	if (IS_ERR(calculator_dev->device)) {
		pr_err("device_create fail\n");
		goto device_create_err;
	}

	mutex_init(&calculator_dev->calculator_mutex);

	return 0;

device_create_err:
	class_destroy(calculator_dev->class);
class_create_err:
	cdev_del(&calculator_dev->cdev);
cdev_add_err:
	unregister_chrdev_region(calculator_dev->devno, 1);
alloc_chrdev_err:
	kfree(calculator_dev);
	calculator_dev = NULL;
	return -1;
}

static void __exit calculator_exit(void)
{
	cdev_del(&calculator_dev->cdev);
	unregister_chrdev_region(calculator_dev->devno, 1);
	device_destroy(calculator_dev->class, calculator_dev->devno);
	class_destroy(calculator_dev->class);
	kfree(calculator_dev);
	calculator_dev = NULL;
}

module_init(calculator_init);
module_exit(calculator_exit);

MODULE_DESCRIPTION("calculator");
MODULE_LICENSE("GPL");

Makefile 文件:

KERNELBUILD:=/lib/modules/$(shell uname -r)/build
default:
	make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
	rm -rf *.o *.ko *.mod.c *.markers *.order *.symvers *.exe
obj-m=calculator.o

编译并安装这个驱动:

y@ubuntu:~/project/test$ make 
y@ubuntu:~/project/test$ sudo insmod calculator.ko 

测试文件:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define CALCULATOR_SUM 		1


int main(void)
{
	int fd;
	int ret;
	/* for select use */
	unsigned long var[3];
	fd = open("/dev/calculator", O_RDWR);
	if (fd < 0) {
		printf("/dev/calculator open failed: %s\n", strerror(errno));
		return -1;
	}
	var[0]=10;
	var[1]=50;
	ret = ioctl(fd, CALCULATOR_SUM, &var);
	if (ret < 0) {
			printf("ioctl sum fail\n");
			return -1;
		}
	else
		printf("The result  is %ld\n",var[2]);
		
	close(fd);

	return 0;
}

编译并运行这个程序

y@ubuntu:~/project/test$ gcc -o calculator_test calculator_test.c
y@ubuntu:~/project/test$ sudo ./calculator_test 
The result  is 60

编译过程中出现一些错误,记录一下

/home/y/project/test/calculator.c:45:4: error: implicit declaration of function ‘copy_from_user’ [-Werror=implicit-function-declaration]
    if (copy_from_user(&var,(void __user *)arg, VAR_SIZE)) {

    ^
/home/y/project/test/calculator.c:51:4: error: implicit declaration of function ‘copy_to_user’ [-Werror=implicit-function-declaration]
    if (copy_to_user((void __user *)arg,&var, VAR_SIZE)) {
    ^
cc1: some warnings being treated as errors

定义copy_from_user()和copy_to_user()出错了,这个是因为缺少头文件:

#include <asm/uaccess.h>

添加上去之后,编译通过。

参考书籍:
《Linux设备驱动开发详解4.0内核》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值