文章目录
先奉上代码
模块代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#define MYMAJOR 0
#define NAME "MyModule"
int major;
static int module_open(struct inode *inode, struct file *file)
{
printk("module_open\n");
return 0;
}
static ssize_t module_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
printk("module_write\n");
return 0;
}
static ssize_t module_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
printk("module_read\n");
return 0;
}
static int module_release(struct inode *inode, struct file *file)
{
printk("module_release\n");
return 0;
}
static const struct file_operations fops = {
.open = module_open,
.write = module_write,
.read = module_read,
.release = module_release,
.owner = THIS_MODULE,
};
static int __init module_test(void)
{
printk(KERN_DEBUG "install module_test");
major = register_chrdev(MYMAJOR, NAME, &fops);
if (major < 0)
{
printk(KERN_ERR "register_chrdev failed\n");
return -EINVAL;
}
printk("module_test major: %d\n",major);
}
static void __exit module_ex(void)
{
unregister_chrdev(major, NAME);
printk(KERN_DEBUG "uninstall module_test");
}
module_init(module_test);
module_exit(module_ex);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhou");
(1)register_chrdev字符注册函数的作用是什么?
首先,我们来看看它需要什么参数。第一个参数输入的主设备号,第二参数传入你的驱动名字,第三个参数传入对应的file_operations结构体。
reguster_chrdev成功调用后,驱动就被注册到内核的驱动表当中,本质上是将驱动的file_operations结构体注册到内核的驱动表中。
register_chrdev默认的次设备号是 0.
cd = __register_chrdev_region(major, 0, 256, name);
这个是内部其中的一行代码。第一个传入注射比号,第二个传入次设备号。
register_chrdev_region
函数,可以区分设备的主次设备号。
有了注册,应当也有注销驱动。unregister_chrdev
(2)怎么查看已经注册好的驱动?
输入命令 cat /proc/devices
可以查看。
(3)驱动的设备号本质的作用是什么?
从上图我们可以看到,最左边的一列就是主设备号,从低到高往下排列。主设备号这相当于这一列表的一个索引,最多能存放256个。在调用register_chrdev时,我传入了MYMAJOR,即输入 0,表示内核自动分配一个设备号,它采取的策略是从后往前寻找一个空闲的位置。
有了驱动的设备号,那么,字符设备就能很轻易地找到它对应的驱动,只要设备号对上了。
(4)有了驱动,那怎么调用它的函数?
首先,我们要明确一条的路线:
在应用程序中,操作字符设备文件(open、read、write) -->
字符设备文件的操作已经被重映射到对应的驱动,最终调用的file_operations结构体中的open、read、write。
(5)字符设备文件怎么与注册了的驱动关联上?
在 /dev 的文件夹中,mknod MyModule c 248 0
新建字符类型的文件,248是主设备号,0是次设备号。这样,就跟驱动关联上。设备号与次设备号必须跟驱动的对应上。
应用程序代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define DEV_DIR "/dev/MyModule"
int main(void)
{
int fd;
char buf[10];
//打开字符设备文件
fd = open(DEV_DIR, O_RDWR);
if (fd < 0)
{
perror("open failed!\n");
}
//测试是否能打开对应驱动的write和read函数,即file_operations中的函数
write(fd, "12345", 5); //读写的内容可以随意,此处用不上
read(fd, (char*)buf, 10);
close(fd);
return 0;
}
Makefile编写
obj-m = module_test.o
KER_DIR = /home/iTOP4421/iTop4412_Kernel_3.0 # 开发板Linux内核的位置
PWD = $(shell pwd)
all:
make -C $(KER_DIR) M=$(PWD) modules
arm-linux-gnueabi-gcc -o app app.c
.PHONY:clean
clean:
make -C $(KER_DIR) M=$(PWD) clean