字符设备驱动程序学习笔记二

来源:互联网 发布:淘宝lg生活海外旗舰店 编辑:程序博客网 时间:2024/05/17 10:57
字符驱动程序
1 设备号
   字符设备通过字符设备文件来存取
   ls -l 如果输出的第一列是c标识,说明该文件是字符设备文件
   设备文件项中的两个数分别为 主设备号/次设备号
  
   设备文件与设备驱动通过主设备号建立联系
   次设备号用来分辩操作的哪个设备
   dev_t 用来描述设备号 在linux/types.h中声明,示例代码如下:
typedef __u32 __kernel_dev_t;


typedef __kernel_fd_setfd_set;
typedef __kernel_dev_tdev_t;
  实质为unsigned int 32位整数,高难12位为主设备号,低12位为次设备号
  
  #主设备号 
  MAJOR(dev_t dev)
  #次设备号
  MINOR(dev_t dev)
  #将主设备号和次设备号转化为dev_t
  MKDEV(int major,int minor)
  以上几个宏在linux/kdev_t.h中声明
  示例代码如下:
#define MAJOR(dev)((dev)>>8)
#define MINOR(dev)((dev) & 0xff)
#define MKDEV(ma,mi)((ma)<<8 | (mi))






  linux内核有两种分配设备号的方法
   1 静态申请
    根据Documentation/devices.txt 确定设备号是否使用
    使用register_chardev_region函数来注册设备号,在linux/fs.h中声明
    extern int register_chrdev_region(dev_t from, unsigned count, const 


char *name);
     form: 需要申请的设备号
     count:设备号数目
     name:设备名
 


   2 动态分配(推荐使用)
    使用alloc_chardev_region函数来注册设备号,在linux/fs.h中声明
extern int alloc_chrdev_region(dev_t *dev, unsigned baseminor, 


unsigned count, const char *name);
      dev:分配的设备号
      baseminor: 起始次设备号
      count:设备号数目
      name:设备名
    缺点:无法在安装驱动前创建设备文件
          安装驱动后,从/proc/devices中查询设备号


 注:对于动态分配设备号的设备驱动程序,对insmod的调用可以替换为一个角
     本,角本的主要内容:
        1 调用insmod
        2 在调用insmod后读取proc/devices取的新分配的主设备号
        3 创建设备文件
可以使用awk工具取得proc/devices信息
          示例代码如下:
        #!/bin/sh
        driver="test"

        echo "角本执行 ...wait"
#读取/proc/devices信息
        major=$(awk "{if(\$2==\"$driver\") {print \$1}}" /proc/devices)
        echo $major
        #创建设备文件
mknod /dev/$driver0 c $major 0

 注销设备号
  void unregister_chrdev_region(dev_t from,unsigned count)
  form:起始设备号
  count:设备号数目


 


2 创建设备文件
  


   mknod命令手工创建
    mknod filename type major minor
    filename:设备文件名
    type:设备文件类型
    major: 主设备号
    minor: 次设备号




   自动创建




3 设备注册


4 重要数据结构
  #打开一个文件
  struct file 
    重要成员:


       /*文件模式*/
mode_t f_mode
 
       /*文件读写位置*/
       loff_t f_pos   
   
       /*与文件相关的操作*/
       struct file_operations *f_op
  
       /*文件标志*/
       unsigned int f_flags
 
       /*跨系统调用时保存状态信息 */
       void *private_data
       
       /*文件对应的目录项结构*/
       struct dentry *f_dentry




  #用来记录文件物理上的信息 
  #一个文件可以对应多个file结构,但只有一个inode结构
  struct inode
    重要成员:
/*设备号*/
        dev_t i_rdev
        
        /*字符设备内核的内部结构*/
  sturct cdev *i_cdev
     /*从一个inode中取得主从设备号最好使用以下两个宏*/
     unsigned int iminor(struct inode *inode);
     unsigned int imajor(struct inode *inode)


  #一个函数指针的集合,定义能在设备上进行的操作,在linux/fs.h中声明
  struct file_operations
  示例代码如下;
/*
 * 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 **);
};






static const struct file_operations mem_fops = {
.llseek = memory_lseek,
.read = read_mem,
.write = write_mem,
.mmap = mmap_mem,
.open = open_mem,
.get_unmapped_area = get_unmapped_area_mem,
};




应用程序访问驱动程序的过程
 read_write.c示例代码如下:
 读文件
 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, 


loff_t *pos){
   ...
  /*f_op是file_operations,就是调用其中的read方法来完成*/
  ret = file->f_op->read(file, buf, count, pos);
   ...
}


 
5 设备注册
  字符设备是用struct cdev 来描述
   字符设备的注册分为以下三步;
1 分配cdev
    struct cdev *cdev_alloc(void)
 
    2 初始化cdev
           void cdev_init(struct cdev *cdev,const struct 


file_operstions *fops)
           cdev:要初始化的cdev结构
           fops:设备对应的操作函数集


        3 添加cdev
           int cdev_add(struct cdev *p,dev_t dev,unsigned count)
           p:要添加的字符设备结构
           dev:设备号
           count:添加设备的个数 
  设备注销
   int cdev_del(struct cdev *p)
   p:要注销的字符设备结构




6 设备操作
   /*完成一些初始化工作
       检查设备特定的错误码
       首次打开则进行初始化
       更新f_op指针
       分配填写filp->private_data数据结构
   */
   int (*open)(struct inode *,sturct file *)
      
   /*设备关闭时调用这个操作*/
   void (*release)(struct inode *,struct file *)






   /*从设备中读取数据*/
   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 xxx_read (struct file *filp,char__user *buff,size_t 


count,loff_t *offp)     
   ssize_t xxx_write (struct file *filp,const char__user *buff,size_t 


count,loff_t *offp)
    filp:文件指针
    count:请求传输的数据量
    buff:参数指向的数据缓存
    offp:文件当前的访问位置
   注:buff参数是用户空间的指针,不能被内核代码直接引用
       用于访问用户空间的指针的方法有:
int copy_from_user(void *to,const void__user *from,int n);
        int copy_to_user(void __user *to,const void *from,int n);
 


   /*对应select系统调用*/
   unsigned int (*poll) (struct file *,struct poll_table_struct *)  


   /*控制设备*/
   int (*ioctl)(struct inode *,struct file *,unsigned int,unsigned 


long)
   
  /*将设备映射到进程虚拟空间中*/
  int (*mmap) (struct file *,struct vm_area_struct *)
 
  /*修改文件的当前读写位置*/
  off_t (*llseek) (sturct file *, loff_t,int)
原创粉丝点击