当前位置:   article > 正文

驱动学习之字符设备框架(一)_字符设备驱动框架

字符设备驱动框架

day1:2023-3-6

应用程序和驱动的交互原理

1)Linux内核和驱动程序运行在内核空间,应用程序运行在用户空间
2)应用程序访问内核空间的三种方式:系统调用 软件中断 硬件中断

字符设备驱动框架

1)linux驱动可以编译到kernel中,也就是zImage,也可以编译为模块.ko,测试的时候只需要加载模块

驱动模块的加载与卸载

1)驱动加载:insmod和modprobe
insmod加载简单驱动,
modprobe加载有依赖关系的驱动,在新linux系统内加载时,要先运行depmod命令
2)卸载命令:rmmod
3)查看加载的驱动命令:lsmod
4)查看内存缓存信息:dmesge |tail -10 查看最后十条缓存信息
5)printk打印级别:
如果不设置打印级别默认级别是4

#define KERN_SOH "\001"
#define KERN_EMERG KERN_SOH "0" /* 紧急事件,一般是内核崩溃 */
#define KERN_ALERT KERN_SOH "1" /* 必须立即采取行动 */
#define KERN_CRIT KERN_SOH "2" /* 临界条件,比如严重的软件或硬件错误*/
#define KERN_ERR KERN_SOH "3" /* 错误状态,一般设备驱动程序中使用KERN_ERR 报告硬件错误 */
#define KERN_WARNING KERN_SOH "4" /* 警告信息,不会对系统造成严重影响 */
#define KERN_NOTICE KERN_SOH "5" /* 有必要进行提示的一些信息 */
#define KERN_INFO KERN_SOH "6" /* 提示性的信息 */
#define KERN_DEBUG KERN_SOH "7" /* 调试信息 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

一共定义了 8 个级别,其中 0 的优先级最高,7 的优先级最低。如果要设置消息级别,参
考如下示例:

printk(KERN_EMERG "gsmi: Log Shutdown Reason\n");
  • 1

字符设备的注册与注销

1)我们想系统注册一个字符设备,使用函数

int register_chrdev(unsigned int major, const char *name,
				  const struct file_operations *fops);
  • 1
  • 2

注销字符设备,使用函数

void unregister_chrdev(unsigned int major, const char *name)
  • 1

Linux 中每个设备都有一个设备号,设备号由主设备号和次设备号两部分组成,主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备。Linux 提供了一个名为 dev_t 的数据类型表示设备号,dev_t 其实就是 unsigned int 类型,是一个 32 位的数据类型。这 32 位的数据构成了主设备号和次设备号两部分,其中高 12 位为主设备号,低 20 位为次设备号。主设备号是2^(12)=4096,范围[0,4095]。
在include/linux/kdev_t.h中定义了设备号的操作

#define MINORBITS 20 //宏 MINORBITS 表示次设备号位数,一共是 20 位。
#define MINORMASK ((1U << MINORBITS) - 1)//宏 MINORMASK 表示次设备号掩码。
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))//宏 MAJOR 用于从 dev_t 中获取主设备号,将 dev_t 右移 20 位即可。
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))//宏 MINOR 用于从 dev_t 中获取次设备号,取 dev_t 的低 20 位的值即可。
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))//宏 MKDEV 用于将给定的主设备号和次设备号的值组合成 dev_t 类型的设备号。
  • 1
  • 2
  • 3
  • 4
  • 5

1、设备号分配
1)静态分配设备号
先用cat /proc/devices查看系统中已使用的设备号

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

应用程序编写

man手册:
1、Standard commands (标准命令)
2、System calls (系统调用)
3、Library functions (库函数)
4、Special devices (设备说明)
5、File formats (文件格式)
6、Games and toys (游戏和娱乐)
7、Miscellaneous (杂项)
8、Administrative Commands (管理员命令)
9、其他(Linux特定的), 用来存放内核例行程序的文档。

驱动程序完善

要求:应用程序可以对驱动进行读写操作,读的话就是应用程序从驱动里读取数据,写的话就是向驱动里写入数据
1)char chrdevbase_read驱动函数编写
驱动给应用传递数据的时候用到copy_to_user函数。
从用户空间向内核空间写数据操作cpoy_form_user函数
argv[]内的对象都是字符串,字符串转int用到了函数atoi()函数

  • 字符设备框架要多练习!多练习!多练习!重要的话说三遍……
/******* kernel driver ******/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
/***************************************************************
文件名		: chrdevbase.c
作者	  	: chise0519
描述	   	: chrdevbase驱动框架文件。
说明		: 部分代码复制byzuozhongkai老师
***************************************************************/

#define CHRDEVBASE_MAJOR	200		/* 主设备号 */
#define CHRDEVBASE_NAME		"chrdevbase" 	/* 设备名     */

static char readbuf[100] = {0};			/* 读缓冲区 */
static char writebuf[100] = {0};		/* 写缓冲区 */
static char kerneldata[] = {"kernel data"}; 	/*kernel data */

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 			一般在open的时候将private_data指向设备结构体。
 * @return 		: 0 成功;其他 失败
 */
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
	printk("chrdevbase open!\r\n");
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - count 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t count, loff_t *offt)
{
	int ret = 0;
	memcpy(readbuf,kerneldata,sizeof(kerneldata));
	ret = copy_to_user(buf,readbuf,count);
	if(ret == 0)
	{
		printk("read kernel data sucess!");
	}else{
		printk("read kernel data failed!");
	}
	//printk("chrdevbase read!\r\n");
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - count 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t count, loff_t *offt)
{
	int ret = 0;
	ret = copy_from_user(writebuf,buf,count);
	if(ret == 0)
	{
		printk("write to kernel data sucess");	
	}else{
		printk("write to kernel data failed");	
	}		
	//printk("chrdevbase write!\r\n");
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
	printk("chrdevbase release!\r\n");
	return 0;
}

/*
 * 设备操作函数结构体
 */
static struct file_operations chrdevbase_fops = {
	.owner		= THIS_MODULE,	
	.open		= chrdevbase_open,
	.read		= chrdevbase_read,
	.write		= chrdevbase_write,
	.release	= chrdevbase_release,
};

/*
 * @description	: 驱动入口函数 
 * @param 		: 无
 * @return 		: 0 成功;其他 失败
 */
static int __init chrdevbase_init(void)
{
	int retvalue = 0;

	/* 注册字符设备驱动 */
	retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
	if(retvalue < 0){
		printk("chrdevbase driver register failed\r\n");
	}
	printk("chrdevbase init!\r\n");
	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit chrdevbase_exit(void)
{
	/* 注销字符设备驱动 */
	unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
	printk("chrdevbase exit!\r\n");
}

/* 
 * 将上面两个函数指定为驱动的入口和出口函数 
 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

/* 
 * LICENSE和作者信息
 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chise0519");


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144

/******* Makefile ******/
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build   
PWD := $(shell pwd)
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
else
	obj-m := chrdevbase.o
endif

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

/******* application source core ******/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

/*
*argc:参数个数
*argv[1]:驱动文件 cd /dev
*argv[2]:读是1,写是2
*/
int main(int argc,char *argv[])
{
    int fd = 0;
	int ret = 0;
    char *filename = argv[1];
	char readbuf[100] = {0};
	char writebuf[100] = {0};
    char usrdata[20] = "this is usrdata!";
    fd = open(filename, O_RDWR);
    /* open */
    if(fd < 0)
    {
        printf("%s file open failed!\r\n",filename);
        return fd;
    }
	if(atoi(argv[2]) == 1)
	{
    	/* read */
    	ssize_t ret = 0;
    	ret = read(fd, readbuf, 20);
    	if(ret == 0)
    	{
    	    printf("%s file read sucess!\r\n",filename);
    	    return -1;
    	}else{
			printf("APP read data:%s\r\n",readbuf);
		}
	}
	
	if(atoi(argv[2]) == 2)
	{
    	/* write */
		memcpy(writebuf,usrdata,sizeof(usrdata));
    	ret = write(fd, writebuf, 20);
    	if(ret < 0)
    	{
    	    printf("%s file write failed!\r\n",filename);
    	    return -1;
    	}else{
			printf("write into kernel data :%s\r\n",usrdata);
		}
	}

    /* close */
    ret = close(fd);
    if(ret < 0)
    {
        printf("%s file close failed!\r\n",filename);
        return -1;
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/180464
推荐阅读
相关标签
  

闽ICP备14008679号