1、Linux三大设备驱动
字符设备:IO的传输过程是以字符为单位的,没有缓冲。比如I2C,SPI都是字符设备。
块设备:IO传输过程是以块为单位的。跟存储相关的,都属于块设备,比如tf卡。
网络设备:与前两个不一样,是以socket套接字来访问的。
①杂项设备驱动是属于字符设备的一种。可以自动生成设备节点。可以输入cat /proc/misc命令查看有哪些杂项设备。
②杂项设备出来比字符设备代码简单。其他区别是杂项设备的主设备号是相同的,均为是10,次设备号是不同的。主设备号相同就可以节省内核的资源。
③主设备号和次设备号是什么?
设备号包含主设备号和次设备号,主设备号在Linux系统里面 是唯一的,次设备号不一定唯一。
设备号是计算机识别设备的一种方式,主设备号相同的被视为同一类设备。
主设备号比做成电话号码的区号。比如北京的区号是010
次设备号可以比做成电话号码。
主设备号可以通过命令cat /proc/devices来查看
2、杂项设备结构体
①定义在内核源码路径: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 struct attribute_group **groups; const char *nodename; umode_t mode; };
3、文件操作集结构体
① 文件操作集结构体是在:include\linux/fs.h
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 *); int (*iterate_shared) (struct file *, struct dir_context *); 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 (*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 ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64); ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, u64); };
注:里面的一个结构体成员都对应一个应用。
②注册杂项设备函数:
extern int misc_register(struct miscdevice *misc);
③注销杂项设备函数:
extern void misc_deregister(struct miscdevice *misc);
4、注册杂项设备的流程
①填充miscdevice这个结构体
②填充file_operations这个结构体
③注册杂项 设备并生产设备节点
5、实验代码
#include <linux/init.h>
#include <linux/module.h>//最基本的文件,支持动态添加和卸载模块
#include <linux/miscdevice.h>//注册杂项设备头文件
#include <linux/fs.h>
#include <linux/uaccess.h>
ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
printk("misc_read \n");
return 0;
}
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
printk("misc_write \n");
return 0;
}
int misc_open(struct inode *inode, struct file *file)
{
printk("misc_open \n");
return 0;
}
int misc_release(struct inode *inode, struct file *file)
{
printk("misc_release \n");
return 0;
}
struct file_operations misc_fops={
.owner = THIS_MODULE,//owner 指针指向的就是你的模块
.open = misc_open,
.release = misc_release,
.read = misc_read,
.write = misc_write,
};
struct miscdevice misc_dev={
.minor=MISC_DYNAMIC_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 ret;
}
printk("misc register is succeed \n");
return 0;
}
static void misc_exit(void)
{
misc_deregister(&misc_dev);
printk("misc_exit \n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
makefile:
obj-m += misc.o #-m的意思是把驱动编译成模块
KDIR := /home/xxx/imx6ull/kernel #内核路径
PWD?=(shell pwd) #获取当前目录
all:
make -C $(KDIR) M=$(PWD) modules
make前设置编译环境变量:
export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-uclibcgnueabihf-
make 就会进入内核源码的路径,然后把当前路径下的代码编译成模块。编译成模块后加载发现生成设备节点: