看着韦东山的视频学arm,跟着他写了一下代码,由于跟视频中所用的视频不一样,所以会出现一下错误,在这里把它记录下来。
先贴上代码:
#include <linux/module.h>//对用模块的使用
#include <linux/kernel.h>//内核头文件,含有一些内核常用文件的原型定义
#include <linux/fs.h>//文件系统头文件,定义文件表结构
#include <linux/init.h>// _init _exit等
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include<linux/device.h> //由于要自动创建结点,所以需要这个头文件
static struct class *firstdrv_class; //创建一个类
static struct device *firstdrv_class_dev; //创建一个设备
static int first_drv_open(struct inode *inode, struct file *file)
{
printk("first_drv_open\n");
return 0;
}
static ssize_t first_drv_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)//__user前面是两横的
{
printk("first_drv_write\n");
return 0;
}
static struct file_operations first_drv_fops= //用来告诉内核的方法
{
.owner=THIS_MODULE,
.open=first_drv_open,
.write=first_drv_write,
};
int major;
int first_drv_init(void) //注册驱动
{
major=register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_class_dev=device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");
return 0;
}
void first_drv_exit(void) //卸载驱动
{
unregister_chrdev(major,"first_drv");
device_destroy(firstdrv_class,MKDEV(major,0));
class_destroy(firstdrv_class);
}
module_init(first_drv_init); //驱动入口
module_exit(first_drv_exit);//驱动出口
MODULE_LICENSE("GPL");
接下来来剖析一下整个流程:
一:首先是头文件的问题,因为版本的不同,所以头文件会不一样,在写之前我先百度了一下linux2.6.30.4内核的头文件(我用的内核),一些没有的我在通过内核的源文件去查找。例如,device_create和class_create相关的头文件,我就是通过源代码找到的。
推荐用sourcesight看源代码,查找的时候会很方便。
二:函数的不同:
还是device_create这些函数,以前的版本会是class_devece_create这些等等。出现这些问题的时候,也只能百度一下,然后看看源代码了。我一直相信我遇到的问题应该别人也有遇到过,百度也就会有。
三:程序的流程:
整个程序很简单。就只是通过open和write两个函数来输出一串字符:
首先,先编写open和write函数,也就是我们想实现的功能都在这里实现。写完之后,问题就来了,我们怎么让内核知道,我们所写的驱动就是用这两个函数呢?
这就涉及到了告诉内核,让内核知道的问题了。
怎么让内核知道:
一、通过一个注册一个file_operations结构,把我们写的程序填充进去。
static struct file_operations first_drv_fops= //用来告诉内核的方法
{
.owner=THIS_MODULE,
.open=first_drv_open,
.write=first_drv_write,
};
二、在内核中注册这个驱动,也就是告诉内核:
int major;
int first_drv_init(void) //注册驱动
{
major=register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_class_dev=device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");
return 0;
}
这个程序中,用到了自动创建设备结点的功能,如果不是自动创建结点的话,还需要手动创建,手动创建的方法就是:
1、加载驱动后, 通过 cat /proc/devices 来查看这个驱动的主设备好,次设备号,还有属性
2、mknod 名字 属性 主设备号 次设备号 来创建结点
但是这个方法比较码放,所以就又有了主动创建结点的功能:
1:先创建出两个结构体,当然,这个根据版本的不同也不一样,具体看源代码
static struct class *firstdrv_class; //创建一个类
static struct device *firstdrv_class_dev; //创建一个设备
2:创建出这个类,还有在这个类下面的设备
major=register_chrdev(0,"first_drv",&first_drv_fops);
firstdrv_class = class_create(THIS_MODULE,"firstdrv");
firstdrv_class_dev=device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");
卸载的时候:
unregister_chrdev(major,"first_drv");
device_destroy(firstdrv_class,MKDEV(major,0));
class_destroy(firstdrv_class);
3、原理:mdev
所谓的mdev就是在注册和卸载驱动的时候,都会在/sys这个目录下生成相应的信息,mdev机制就可以通过这个信息来生成一个类,然后再生成设备结点。
通过上面的语句,注册成功后,系统就会根据相应的信息在创建出这个设备结点。可以在 /sys/class找到first_drv这个类,在这个类的下面就是“xyz”。
生成的这个设备结点,是在/dev/xyz中的。不用混乱了,上面的只是设备的信息而已,真正的设备结点在/dev这个目录下
三、怎么让系统知道就是这个注册函数;
系统就是通过下面的这两条语句来分辨不同驱动的注册函数的:
module_init(first_drv_init); //驱动入口
module_exit(first_drv_exit);//驱动出口
整个驱动的编写流程结束了。
接下来说一下测试程序:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main(int argc,char **argv)
{
int fd;
int val=1;
fd=open("/dev/xyz",O_RDWR);
if(fd<0)
printf("can't open!\n");;
write(fd,&val,4);
return 0;
}
/dev/xyz 就是我们之前创建的设备结点。别看到名字就以为系统是通过名字来识别他响应的驱动程序的。其实不是的,名字是可以一样的,唯一不一样的只有设备号。
所以是通过设备号还有属性来找到相应的设备的。例如如果/dev/xyz的设备号是252,属性是字符,那么他相应的设备号和属性也是一样的。这样就将系统程序与驱动给对应起来了,以为是很简单的函数,就不加以解释了。
哇咔咔,搞定,我的第一个arm驱动程序。