linux字符设备驱动入门模版讲解

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函数)。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值