九鼎RK3399笔记三:测试迅为电子的杂项设备驱动
Linux三大设备驱动
字符设备:IO的传输过程是以字符为单位的,没有缓冲。比如I2C,SPI都是字符设备。
块设备:IO的传输过程是以块为单位的。跟存储相关的,都属于块设备,比如TF卡。
网络设备:与前两个不一样,是以socket套接字来访问的。
杂项设备(misc device)
1、 本小节讲述的杂项设备是属于字符设备的一种。可以自动生成设备节点。
我们的系统中有许多杂项设备,可以输入cat /proc/misc来查看。
2、杂项设备除了比字符设备代码简单,还有什么区别?
杂项设备的主设备号是相同的,均为10,次设备号是不同的。主设备号相同就可以节省内核的资源。
3、主设备号和次设备号是什么?
设备号包括主设备号和次设备号,主设备号在Linux系统里是唯一的,次设备号不一定唯一。
设备号是计算机识别设备的一种方式,主设备号相同的就被视作为同一类设备。
主设备号可以比作成电话号码的区号,比如北京的区号是010.
次设备号可以比作电话号码。
主设备号可以通过命令cat /proc/devices来查看。
4、杂项设备的描述
定义在内核源码路径:include/linux/miscdevice.h
struct miscdevice {
int minor;//次设备号
const char *name;//设备节点名字
const struct file_operations *fops;//文件操作集
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
include/linux/fs.h讲述了文件操作集的结构体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 (*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);
};
里面的一个结构体成员都要对应一个调用。
extern int misc_register(struct miscdevice * misc);//注册杂项设备
extern int misc_deregister(struct miscdevice *misc);//注销杂项设备
5、注册杂项设备的流程
(1)填充miscdevice这个结构体。
(2)填充file_operations这个结构体。
(3)注册杂项设备并生成设备节点。
杂项设备注册实践
按照5中讲述的注册杂项设备的流程实现代码misc.c
#include<linux/init.h>
#include<linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
struct file_operations misc_fops =
{
.owner = THIS_MODULE
};
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello_misc",
.fops = &misc_fops
};
static int misc_init(void)
{
int ret;
ret = misc_register(&misc_dev);//注册
if (ret<0)
{
printk("misc_register is error\n");
return -1;
}
printk("misc_register is successful\n");
return 0;
}
static void misc_exit(void)
{
misc_deregister(&misc_dev);//卸载
printk("bye bye misc\n");
return;
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
使用如下的Makefile,编译成外部模块(不和内核一起编译)
# -m 的意思是把我们的驱动编译成模块,后面接文件名.o
obj-m += misc.o
KDIR:=/test/doc/x3399/x3399_linux_new/kernel/
#获取当前目录的变量
PWD?=$(shell pwd)
#make 会进入内核源码的路径,然后把当前路径下的代码编译成模块
#make 指令前面必须按tab,不能用4个空格代替
all:
make -C $(KDIR) M=$(PWD) modules
使用如下的自动编译脚本
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make
编译成功后,加载模块到板子上
insmod misc.ko
查看现象,ls /dev/路径下有hello_misc
这样,一个最简单的杂项设备就完成了。