linux驱动学习记录(一)-字符设备框架

该系列是我在学习宋宝华老师的《Linux设备驱动开发详解》以及结合其他网上教程所做记录。


1.     Linux设备

 

在Linux操作系统下的设备通常分为三类:

字符设备、块设备和网络设备。

 

字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。

块设备则是利用一块系统内存作为缓冲区,当用户进程对设备进行读写请求时,驱动程序先查看缓冲区中的内容,如果缓冲区中的数据能满足用户的要求就返回相应的数据,否则就调用相应的请求函数来进行实际的I/O操作。块设备主要是针对磁盘等慢速设备设计的,其目的是避免耗费过多的CPU时间来等待操作的完成。

网络设备不同于字符设备和块设备,它是面向报文的而不是面向流的,它不支持随机访问,也没有请求缓冲区。在Linux里一个网络设备也可以叫做一个网络接口,如eth0,应用程序是通过Socket而不是设备节点来访问网络设备,在系统里根本就不存在网络设备节点。

一般说来,PCI卡属于字符设备。

 

 

2.     字符设备驱动框架


2.1   设备驱动模块

 

在以模块方式编写驱动程序时,要实现两个必不可少的函数init_module()和cleanup_module(),而且至少要包含<linux/krernel.h>和<linux/module.h>两个头文件。

static int __init xxx_init_module (void)
{
    ...
}

static void __exit xxx_cleanup_module (void)
{
	...
}
/* 驱动模块加载函数 */
module_init(xxx_init_module);
/* 驱动模块卸载函数 */
module_exit(xxx_cleanup_module);

2.2   字符设备驱动的两个重要结构体

 

cdev

在Linux内核中,使用cdev结构体描述一个字符设备,cdev结构体的定义如下

struct cdev {

struct kobject kobj; /* 内嵌的kobject对象 */

  struct module *owner; /* 所属模块*/

struct file_operations *ops; /* 文件操作结构体*/

struct list_head list;

dev_t dev; /* 设备号*/

unsigned int count;

};

cdev结构体的dev_t成员定义了设备号,为32位,其中12位为主设备号,20位为次设备号。使用下列宏可以从dev_t获得主设备号和次设备号:

MAJOR(dev_t dev)

MINOR(dev_t dev)

而使用下列宏则可以通过主设备号和次设备号生成dev_t:

MKDEV(int major, int minor)

 

file_operations

cdev结构体的另一个重要成员file_operations定义了字符设备驱动提供给虚拟文件系统的接口函数。

file_operations结构体中的成员函数是字符设备驱动程序设计的主体内容,这些函数实际会在应用程序进行Linux的open()、write()、read()、close()等系统调用时最终被内核调用。file_operations结构体的定义如下

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 (*iterate) (struct file *, struct dir_context *);

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 *, loff_t, loff_t, 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);

 int (*show_fdinfo)(struct seq_file *m, struct file *f);

};

 

 

2.3   字符设备驱动模型


以下为一个简单字符驱动框架示例代码,没有任何功能

 

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         //预定义主设备号 #define LS_MAJOR 150 static int ls_major = LS_MAJOR; module_param(ls_major, int, S_IRUGO); //自定义设备结构体 struct ls_dev { struct cdev cdev; //在Linux内核中,使用cdev结构体描述一个字符设备 }; struct ls_dev *ls_devp; /* 字符设备file_operations open 成员函数 */ static int xxx_open(struct inode *inode, struct file *filp) { return 0; } /* 字符设备file_operations ioctl 成员函数 */ static long xxx_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { return 0; } /* 字符设备file_operations read 成员函数 */ static ssize_t xxx_read(struct file *filp, char __user * buf, size_t size, loff_t * ppos) { return 0; } /* 字符设备file_operations write成员函数 */ static ssize_t xxx_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos) { return 0; } /* 字符设备file_operations release成员函数 */ static int xxx_release(struct inode *inode, struct file *filp) { return 0; } /* 设备文件操作接口 */ static const struct file_operations xxx_fops = { .owner = THIS_MODULE, /* xxx_fops 所属的设备模块 */ .read = xxx_read, /* 读设备操作*/ .write = xxx_write, /* 写设备操作*/ .unlocked_ioctl = xxx_ioctl, /* 控制设备操作*/ .open = xxx_open, /* 打开设备操作*/ .release = xxx_release, /* 释放设备操作*/ }; static int __init xxx_init_module (void) { //申请字符设备号 dev_t xxx_dev_no = MKDEV(ls_major, 0); register_chrdev_region(xxx_dev_no, 1, "driver_test"); //为自定义设备结构体申请内存 ls_devp = kzalloc(sizeof(struct ls_dev), GFP_KERNEL); //将自定义设备结构体内的cdev成员与file_operations设备文件操作接口绑定 cdev_init(&ls_devp->cdev,&xxx_fops); //拥有该结构的模块的指针,一般为THIS_MODULES ls_devp->cdev.owner = THIS_MODULE; //注册设备 cdev_add(&ls_devp->cdev, xxx_dev_no, 1); return 0; } static void __exit xxx_cleanup_module (void) { /* 注销字符设备 */ cdev_del(&ls_devp->cdev); /* 释放占用的设备号 */ unregister_chrdev_region(MKDEV(ls_major, 0), 1); kfree(ls_devp); } /* 驱动模块加载函数 */ module_init(xxx_init_module); /* 驱动模块卸载函数 */ module_exit(xxx_cleanup_module); MODULE_AUTHOR("LuoSheng"); MODULE_LICENSE("GPL v2"); 
       
      
      
     
     
    
    
   
   


3.      驱动加载

 

可以使用命令insmod载入Linux内核,从而成为内核的一个组成部分,此时内核会调用模块中的函数init_module( )。当不需要该模块时,可以使用rmmod命令进行卸载,此进内核会调用模块中的函数cleanup_module()。任何时候都可以使用命令来lsmod查看目前已经加载的模块以及正在使用该模块的用户数。 

所有已经注册(即已经加载了驱动程序)的硬件设备的主设备号可以从/proc/devices文件中得到。使用mknod命令可以创建指定类型的设备文件,同时为其分配相应的主设备号和次设备号。例如,下面的命令:

#insmod pcitest.ko //加载驱动

#mknod /dev/pcitest c 150 0 //创建设备文件,主设备号150,次设备号0

#rmmod pcitest  //卸载驱动

 

将建立一个主设备号为150,次设备号为0的字符设备文件/dev/ pcitest。

当应用程序对某个设备文件进行系统调用时,Linux内核会根据该设备文件的设备类型和主设备号调用相应的驱动程序,并从用户态进入到核心态,再由驱动程序判断该设备的次设备号,最终完成对相应硬件的操作。 


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值