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" /* 调试信息 */
一共定义了 8 个级别,其中 0 的优先级最高,7 的优先级最低。如果要设置消息级别,参
考如下示例:
printk(KERN_EMERG "gsmi: Log Shutdown Reason\n");
字符设备的注册与注销
1)我们想系统注册一个字符设备,使用函数
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops);
注销字符设备,使用函数
void unregister_chrdev(unsigned int major, const char *name)
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、设备号分配
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);
};
应用程序编写
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");
/******* 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*
/******* 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;
}
935

被折叠的 条评论
为什么被折叠?



