个人笔记
1、系统整体工作原理
(1)应用层->API->设备驱动->硬件
(2)API:open、read、write、close等
(3)驱动源码中提供真正的open、read、write、close等函数实体
2、 file_operations 结构体
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 (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsignedlong);
long (*compat_ioctl) (struct file *, unsigned int, unsignedlong);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*mremap)(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 **, void
**);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
};
file_operations 的结构体是 Linux 内核驱动操作函数集合,在做linux驱动时,要构建此结构体,要在其中构造好函数与放在该结构体之外的函数做好联系才能够调用函数。
static struct file_operations chardevbase_fops={
.owner = THIS_MODULE,
.open = chrdevbase_open,
.release = chrdevbase_release,
.read = chrdevbase_read,
.write = chrdevbase_write,
};
当然并不是要全部都要写,只写好需要调用的函数就可以了。
3、驱动模块的加载与卸载
Linux 驱动有两种运行方式,第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启
动的时候就会自动运行驱动程序。第二种就是将驱动编译成模块(Linux 下模块扩展名为.ko),在
Linux 内核启动以后使用“insmod”命令加载驱动模块。平时自己调试的时候用第二种很方便,利用nfs把程序发送到板子上再运行即可。
模块有加载和卸载两种操作,我们在编写驱动的时候需要注册这两种操作函数,模块的加载和
卸载注册函数如下:
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
static int __init chrdevbase_init(void)
{
int ret=0;
printk("hello\r\n");
ret = register_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME,&chardevbase_fops);
if(ret < 0){
printk("error\r\n");
}
return 0;
}
static void __exit chrdevbase_exit(void)
{
printk("over\r\n");
unregister_chrdev(CHRDEVBASE_MAJOR,CHRDEVBASE_NAME);
}
//模块注册
module_init(chrdevbase_init);
//模块卸载
module_exit(chrdevbase_exit);
驱动编译完成以后扩展名为.ko,使用modprobe命令可以加载驱动模块是最简单的模块加载命令,此命令用于加载指定的.ko 模块
modprobe drv.ko
驱动模块的卸载使用命令“rmmod”即可,比如要卸载 drv.ko,使用如下命令即可:
rmmod drv.ko
也可以使用“modprobe -r”命令卸载驱动,比如要卸载 drv.ko,命令如下:
modprobe -r drv.ko
使用 modprobe 命令可以卸载掉驱动模块所依赖的其他模块,前提是这些依赖模块已经没
有被其他模块所使用,否则就不能使用 modprobe 来卸载驱动模块。所以对于模块的卸载,还是
推荐使用 rmmod 命令
对于字符设备驱动而言,当驱动模块加载成功以后需要注册字符设备,同样,卸载驱动模
块的时候也需要注销掉字符设备。字符设备的注册和注销函数原型如下所示:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)
般字符设备的注册在驱动模块的入口函数 xxx_init 中进行,字符设备的注销在驱动模块
的出口函数 xxx_exit 中进行。
4、设备号
设备号的组成:
设备号是由一个 32 位的数据类型。这 32 位的数据构成了主设备号和次设备号两部分,其中高 12 位为主设备号,低 20 位为次设备号。因此 Linux系统中主设备号范围为 0~4095。
设备号的分配:
分为动态和静态两种分配方式,静态是开发者自己查看设备号后选择没有被分配的设备来分配。动态是由系统自动来分配的。
5、编译驱动程序和测试 app
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int ret = 0;
int fd = 0;
char *filename;
char readbuf[100];
char writebuf[100];
static char usrdata[]={"user data\r\n"};
if(argc != 3){
printf("error usage\r\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd < 0){
printf("error\r\n");
return -1;
}
if(atol(argv[2]) == 1){
ret = read(fd,readbuf,50);
if(ret < 0){
printf("open error: %s\r\n",filename);
}
else{
printf("read data:%s\r\n",readbuf);
}
}
if(atol(argv[2]) == 2){
memcpy(writebuf,usrdata,sizeof(usrdata));
ret = write(fd,writebuf,50);
if(ret < 0){
printf("write error %s\r\n",filename);
}
else{
}
}
ret = close(fd);
if(ret < 0){
printf("close error %s\r\n",filename);
}
return 0;
}
输入./chrdevbaseApp /dev/chrdevbase 2即可执行