Chinaunix首页 | 论坛 | 博客
  • 博客访问: 11317
  • 博文数量: 3
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-13 11:43
文章分类
文章存档

2014年(3)

我的朋友
最近访客

分类: LINUX

2014-08-29 18:13:59


/*linux中mmap设备方法程序详解
#include /*mouule.h动态的讲模块加载到内核中去*/
#include
#include
#include    //定义错误码
#include
#include
#include
#include
#include
#include
#include


#include  // printk()
#include "memdev.h"  //memdev.h宏定义主设备号,设备数,分配内存大小,mem
 //设备结构体的描述。
static int mem_major = MEMDEV_MAJOR;


module_param(mem_major, int, S_IRUGO);


struct mem_dev *mem_devp; /*mem设备结构体指针*/


struct cdev cdev; 


/*设备文件打开函数*/
/* 涉及到三个重要的结构体
/* struct file
/*struct inode
/*struct file_operation
/**/


/*mem_open:结构体mem_ops中的一个成员
/*两个成员:inode用于记录文件物理物理上的信息,file对应一个打开的文件
/*功能: 
/*检查设备特定的错误(例如设备没准备好, 或者类似的硬件错误 
/*如果它第一次打开, 初始化设备 
/*如果需要, 更新 f_op 指针. 
/*分配并填充要放进 filp->private_data 的任何数据结构 */


int mem_open(struct inode *inode, struct file *filp)
{
    struct mem_dev *dev; /*定义一个设备结构体指针*/
    
    /*获取次设备号*/
    int num = MINOR(inode->i_rdev); /*inode结构体有一个成员struct cdev *i_rdev 表示一个指向字符设备的结构体指针*/


    if (num >= MEMDEV_NR_DEVS)/*MEMDEV_NR_DEVS一个宏表示设备数*/ 
            return -ENODEV;
    dev = &mem_devp[num];  /*让指针 dev 指向所获取的设备
        /* 的设备描述结构体。
    
    /*将设备描述结构指针赋值给文件私有数据指针*/
    filp->private_data = dev;
    
    return 0; 
}




/***********************************************
/*mem_release():结构体mem_ops中的一个成员
/*功能:对应open()函数,关闭
/*文件释放函数*/
/***************************************/


int mem_release(struct inode *inode, struct file *filp)
{
  return 0;
}


/***************************************************
/*memdev_mmap :结构体mem_ops中的一个成员
/*两个结构体类型的成员: file ,vm_area_struct
/*功能: 讲设备内存映射到进程的地址空间*/
/***************************************************************/


static int memdev_mmap(struct file*filp, struct vm_area_struct *vma)
{
      struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
       // 获得设备结构体的指针,由于
      // read() 函数中没有 struct
      // inode 结构,所以只能通过
      // file->private_data 获得!
      vma->vm_flags |= VM_IO;
      vma->vm_flags |= VM_RESERVED;


     
      if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(dev->data)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
          return  -EAGAIN;
                
      return 0;
}


/*文件操作结构体struct file_operation mem_fops*/
/* 包含了一些操作文件的函数,
/* 以便应用程序对设备进行操作时进行调用。*/


static const struct file_operations mem_fops =
{
  .owner = THIS_MODULE, //拥有该结构体的的模块的指针,一般为THIS_MODULE
  .open = mem_open,   //用于打开设备文件
  .release = mem_release, //关闭设备文件
  .mmap = memdev_mmap, //将设备内存映射领导到进程地址空间
};


/*设备驱动模块加载函数memdev_init*/
/*完成的任务有:
/*1.为设备分配设备号,一般采用动态分配方式 alloc_chrdev_region()
/*2.初始化cdev结构。
/*3.注册字符驱动程序
/*4,为字符驱动程序分配内存空间
/**/
static int memdev_init(void)   //初始化模块
{
  int result;
  int i;


  dev_t devno = MKDEV(mem_major, 0); 
   //MKDEV是将主设备号和次设备号转换为dev_t类型数据,参数mem_major在头文件


  /* 静态申请设备号*/
  if (mem_major)
    result = register_chrdev_region(devno, 2, "memdev");
//devno为主设备号,共申请两个连续的设备,设备名为"memdev"
  else  /* 动态分配设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, 2, "memdev");
//&devno作为一个输出参数,次设备号从0开始分配,申请2个设备,设备名为"memdev"
    mem_major = MAJOR(devno);
//获取动态分配到的主设备号。
  }  
  
  if (result < 0)
    return result;


  /*初始化cdev设备结构结构*/
  cdev_init(&cdev, &mem_fops);
  //初始化cdev结构,将结构体cdev中fops成员和mem_fops绑定起来
  cdev.owner = THIS_MODULE;
  cdev.ops = &mem_fops;
  
  /* 注册字符设备 */
  cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
   
  /* 为设备描述结构cdev分配内存*/
  mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
  //kmalloc函数返回的是虚拟地址(线性地址).
  if (!mem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(mem_devp, 0, sizeof(struct mem_dev));
  //新申请的内存做初始化工作,清零工作
  
  /*为设备mem_dev分配内存*/
  for (i=0; i < MEMDEV_NR_DEVS; i++) 
  {
        mem_devp[i].size = MEMDEV_SIZE; //#define MEMDEV_SIZE 4096
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);//分配内存给两个设备
        memset(mem_devp[i].data, 0, MEMDEV_SIZE);//初始化新分配到的内存
  }
    
  return 0;
  fail_malloc: 
  unregister_chrdev_region(devno, 1); //如果申请失败,注销设备 
  return result;
}


/*模块卸载函数*/
static void memdev_exit(void)
{
  cdev_del(&cdev);   /*注销设备*/
  kfree(mem_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
}


MODULE_AUTHOR("David Xie"); /*描述这个模块作者*/
MODULE_LICENSE("GPL"); //定义这个模块所用的许可证


module_init(memdev_init); //程序的入口是:module_init();加载模块到内核时调用函数 memdev_init
module_exit(memdev_exit); // 程序的出口:moule_exit();删除模块时调用函数memdev_exit

阅读(1078) | 评论(0) | 转发(0) |
0

上一篇:mini2440按键驱动

下一篇:printk级别问题

给主人留下些什么吧!~~