驱动种类

字符设备
块设备
网络设备

编译到内核 或 编译成模块(.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_MMU
unsigned (*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 register_chrdev_region(dev_t from, unsigned count, const char *name)
/*
参数 from 是要申请的起始设备号,也就是给定的设备号;参数 count 是要申请的数量,一
般都是一个;参数 name 是设备名字。
注销字符设备 之 后 要 释 放 掉 设 备 号 , 不 管 是 通 过 alloc_chrdev_region 函数还是
register_chrdev_region 函数申请的设备号,统一使用如下释放函数:
*/
动态分配
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.15
depmod #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/chrdevbase
retvalue = 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 是要写入的地址。

posted on 2025-09-22 15:28  ycfenxi  阅读(4)  评论(0)    收藏  举报