linux字符设备驱动都有固定的模版,下面我贴出的模版里面有详细的注释。每次写字符设备驱动时,都可以套用此模版,然后稍微修改一下就可以用了。
/*======================================================================
字符设备模版 5/13/2014
The initial developer of the original code is
======================================================================*/
/*
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
*/
/*xxx_dev设备结构体*/
struct xxx_dev
{
struct cdev cdev; /*cdev结构体,每个字符设备都有一个这样的结构体*/
......
};
struct xxx_dev *xxx_devp; /*设备结构体指针*/
/*文件打开函数*/
int xxx_open(struct inode *inode, struct file *filp)
{
return 0;
}
/*文件释放函数*/
int xxx_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctl设备控制函数 */
long xxx_ioctl(struct file *, unsigned int, unsigned long);
{
...
switch (cmd)
{
case xxx_CMD1:
...
break;
case xxx_CMD2:
...
break;
default:
//不能支持的命令
return - ENOTTY;
}
return 0;
}
/*读函数*/
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
copy_to_user(buf,...,...); //unsigned long copy_to_user(void __user *to,const void *from,unsigned long count);
}
/*写函数*/
ssize_t xxx_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
...
copy_from_user(...,buf,...); //unsigned long copy_from_user(void *to,const void __user *from,unsigned long count);
...
}
/* seek文件定位函数 */
static loff_t xxx_llseek(struct file *filp, loff_t offset, int orig)
{
return 0;
}
/*文件操作结构体*/
static const struct file_operations xxx_fops =
{
.owner = THIS_MODULE,
.llseek = xxx_llseek,
.read = xxx_read,
.write = xxx_write,
.unlocked_ioctl=xxx_ioctl, //版本不一样,此函数不一样。在6410中的2.6.38是.unlocked_ioctl
//.ioctl = xxx_ioctl, 在ubunto是2.6.32,是ioctl !!!!!!!!!!!!
.open = xxx_open,
.release = xxx_release,
......
};
/*设备驱动模块加载函数*/
int xxx_init(void)
{
int result;
......
xxx_devp = kmalloc(sizeof(struct xxx_dev), GFP_KERNEL); //申请xxx_dev结构体内存,返回指向结构体指针
cdev_init(&dev->cdev, &xxx_fops); //每个字符设备都有一个字符设备结构体cdev,使cdev和file_operations类型的结构体xxx_fops发生联系
dev_t xxx_dev_no = MKDEV(xxx_major,xxx_minor); //创建设备号
xxx_dev.cdev.owner = THIS_MODULE;
/*
xxx_major 如果初始化为0,就默认选择动态分配,可以在xxx.h中定义
xxx_minor 也要初始化
*/
/*
xxx_major 如果初始化为0,就默认选择动态分配
xxx_minor 也要初始化
*/
/* 申请主设备号*/
if (xxx_major)
{
result = register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
}
else //动态申请主设备号
{
result = alloc_chrdev_region(&xxx_dev_no,0,1,DEV_NAME);
}
if (result < 0){
printk(KERN_WARNING"can't get major %d\n",xxx_major);
return result;
}
/*注册设备,只要cdev_add返回了,我们的设备就“活”,它的操作就会被内核调用。
因此,在驱动程序还没有完全准备好处理设备上的操作时,就不能调用cdev_add*/
result=cdev_add(&xxx_dev.cdev,xxx_dev_no,1);
......
return 0;
}
/*模块卸载函数*/
void xxx_exit(void)
{
cdev_del(&xxx_devp->cdev); /*注销cdev*/
kfree(xxx_devp); /*释放设备结构体内存,其中xxx_devp为结构体指针*/
unregister_chrdev_region(MKDEV(xxx_major,xxx_minor), 1); /*释放设备号*/
......
}
MODULE_AUTHOR("Chen Teng");
MODULE_LICENSE("Dual BSD/GPL");
module_init(xxx_init); //当加载驱动模块时,就会调用xxx_init函数
module_exit(xxx_exit); //当卸载驱动模块时,就会调用xxx_exit函数
/*2.6.39版本的
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 *);
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 *, 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 **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};
*/
下面,再讲解驱动程序与应用程序的调用关系
当我们写好了字符设备驱动之后,要把他加载到内核中。
加载之后,当在应用程序中“open”一个字符设备文件时,就会找到file结构体中的file_operations结构体里面的open成员,然后再通过open :spioc_open找到驱动程序中的spioc_open函数,调用spioc_open函数。这样当我们使用open函数时最终就会调用驱动程序中的“spioc_open”函数(名字无所谓,比如open :xxx_open就调用xxx_open函数)。