驱动学的越到感觉知识越乱,从头开始理清。
下面是看韦东山老师的视频总结的:
写驱动程序主要是搞清楚驱动的框架,
下面是写驱动的步骤:
1. 写驱动的读写等函数,一般应用程序用到哪些就写哪些 。
2.
2.1 定义一个file_operations结构体。
2.2 注册,就是把file_operation结构体告诉内核,使用register_chrdev函数.
3. 谁来调用register_chrdev函数,那就是驱动入口函数。
4. 驱动很多怎么知道当应用程序调用读写函数时调用哪一个驱动的读写函数呢?那就要module_init修饰.
下面是一个例子,功能:当测试程序(应用程序)调用open函数,然后打印一句话。
驱动程序要使用的函数及结构体分析:
/*major:主设备号,如果是0,系统分配主设备号;
*name:设备的名字;
*fops:定义的file_operations结构地址
*/
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
/*注销*/
void unregister_chrdev(unsigned int major, const char *name)
/*应用程序用的read,write都是调用这里的read,write
* NOTE:
* read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
* can be called without the big kernel lock held in all filesystems.
*/
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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
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 *, struct dentry *, 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 **);
};
驱动程序:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/poll.h>
/*arm板中linux系统版本不同,头文件的所在目录会有不同*/
/* OPEN 函数,可以仿照file_operations结构中的open函数的类型来写*/
static int first_drv_open(struct inode *inode,struct file *file){
printk("first_drv_open\n");
return 0;
}
/*write 函数可以仿照file_operations结构中的open函数的类型来写*/
static ssize_t first_drv_write(struct file *file,const char __user *buf,
size_t count,loff_t *ppos)
{
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,无参数,名字可以任意*/
int first_drv_init(void)
{
/*注册,通俗的说就是告诉内核*/
register_chrdev( 111,"first_drv",&first_drv_fops);
return 0;
}
/*程序出口函数,此函数类型是一样的,无返回值,无参数,名字可以任意*/
void first_drv_exit(void)
{
unregister_chrdev( 111,"first_drv");
}
module_init(first_drv_init); //修饰入口函数
module_exit(first_drv_exit); // 修饰出口函数
Makefile:
/*虚拟机下你的arm板移植的linux内核的所在目录*/
KERNELDIR=/home/wangfei/example/linux/mysystem/linux-2.6.30.4
PWD:=$(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.cmd *.ko *.mod.c *.order *.symvers .bak
obj-m:=first_devdri.o
测试程序:
/*用于测试驱动程序*/
#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/xxx",O_RDWR);
if(fd<0){
printf("can't open!\n");
write(fd,&val,4);
return 0;
}
}
下面就是测试了:
1,用 insmod 加载驱动程序
2,手动建立设备节点(下一个博客就是自动创建驱动文件)
设备节点 设备文件名 文件属性 主设备号 次设备号
mknod /dev/xxx c 111 0
我们的应用程序就是通过设备文件和主设备号找到在register_chrdev中的定义,从而找到相应的驱动函数。
insmod 加载驱动程序,
lsmod 查看驱动设备
rmmod 卸载驱动设备