linux驱动开发笔记
驱动种类
字符设备 |
---|
块设备 |
网络设备 |
编译到内核 或 编译成模块(.ko)
一:字符设备
file_operations 的结构体
在 Linux 内核文件 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 *);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 (*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_MMUunsigned (*mmap_capabilities)(struct file *);
#endif
};
2.字符设备编写步骤
2.1驱动模块的入口和出口
module_init(led_init);
module_exit(led_exit);
2.2字符设备注册与注销
__init *(void) & __exit *(void)函数实现
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;
}static void __exit chrdevbase_exit(void){/* 注销字符设备驱动 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase exit!\r\n");
}
2.3实现设备的具体操作函数(file_operations )
2.4设备号分配
linux内核使用dev_t(types.h) 32位,高12位为主设备号,低20位为次设备号。
MDJPOR(dev_t)、MINOR、MKDEV(major,minor)
静态手动分配
有一些常用的设备号已经被 Linux 内核开发者给分配掉
了,具体分配的内容可以查看文档 Documentation/devices.txt。并不是说内核开发者已经分配掉
的主设备号我们就不能用了,具体能不能用还得看我们的硬件平台运行过程中有没有使用这个
主设备号,使用“cat /proc/devices”命令即可查看当前系统中所有已经使用了的设备号。
动态分配
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
/*
dev:保存申请到的设备号。
baseminor:次设备号起始地址,alloc_chrdev_region 可以申请一段连续的多个设备号,这
些设备号的主设备号一样,但是次设备号不同,次设备号以 baseminor 为起始地址地址开始递
增。一般 baseminor 为 0,也就是说次设备号从 0 开始。
count:要申请的设备号数量。
name:设备名字。
*/void unregister_chrdev_region(dev_t from, unsigned count)
/*
from:要释放的设备号。
count:表示从 from 开始,要释放的设备号数量。
*/
2.5添加 LICENSE 和作者信息
/* * LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kai");
2.6驱动模块加载&卸载(linux加载)
cp *.ko /lib/modules/4.1.15depmod #depmod(depend module)可检测模块的相依性,供modprobe在安装模块时使用。modprobe *.ko
lsmod#查看加载的ko
cat /proc/devices #查看所有驱动的设备号
insmod **.ko#(只加载本身ko,不解决ko间的依赖关系)
modprobe #会解决ko之间的依赖关系
rmmod *.ko #卸载ko
modprobe -r *.ko #卸载依赖所有的ko
2.7创建设备节点
mknod /dev/chrdevbase c 200 0 创建节点 c字符型设备 200主设备号,0次设备号
ls /dev/chrdevbase -l”命令查看
2.8app调用
/* 打开驱动文件 */fd = open(filename, O_RDWR);//filenme == /dev/chrdevbaseretvalue = read(fd, readbuf, 50);retvalue = write(fd, writebuf, 50);retvalue = close(fd);
内核空间和应用空间内存访问
用户空间:32位系统中占用0~3GB,64位系统中占用低128T
内核空间:32位系统中占用3~4GB,64位系统中占用高128T
copy_from_user用于将数据从用户空间复制到内核空间:
copy_to_user用于将数据从内核空间复制到用户空间:
ioremap 函数用于获取指 定 物 理 地 址 空 间 对 应 的 虚 拟 地 址 空 间
iounmap 函数释放掉 ioremap 函数所做的映射
I/O 内存访问函数
使用 ioremap 函数将寄存器的物
理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux 内核不建议
这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作。
readb、readw 和 readl 这三个函数分别对应 8bit、16bit 和 32bit 读操作,参数 addr 就是要
读取写内存地址,返回值就是读取到的数据。
writeb、writew 和 writel 这三个函数分别对应 8bit、16bit 和 32bit 写操作,参数 value 是要
写入的数值,addr 是要写入的地址。