linux块设备驱动之实例

原创 2015年07月13日 00:39:18

        前两篇blog已经基本熟悉了块设备的相关结构,这里来总结下ldd3中提到的一些块设备驱动例题;


1、注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动

        sbull_major  =  register_blkdev(sbull_major, "sbull");
        if (0 >=  sbull_major){
            printk(KERN_WARNING "sbull:   unable  to  get  major  number!\n");
            return  -EBUSY;
        }


2、定义设备结构体:

struct sbull_dev{
     int size; // 以扇区为单位,设备的大小
     u8  *data; // 数据数组
     short users; // 用户数目
     short media_change; // 介质改变标识
     spinlock_t lock; // 用于互斥
     struct request_queue  *queue; // 设备请求队列
     struct gendisk  *gd; // gendisk结构
     struct timer_list time; // 用来模拟介质改变
 };


3、初始化设备结构体:

 memset(dev, 0, sizeof(struct sbull_dev));
 dev->size  = nsectors * hardsect_size;
 dev->data  = vmalloc(dev->size);
 if (dev->data == NULL){
     printk(KERN_NOTICE "vmalloc failure.\n");
     return;
 }
 spin_lock_init(&dev->lock);//初始化自旋锁,为了下一步的队列分配

4、创建设备的请求队列:

 dev->queue = blk_init_queue(sbull_request,  &dev->lock);


5、分配、初始化及安装相应的gendisk结构:

 dev->gd = alloc_disk(SBULL_MINORS);
 if (!dev->gd) {
     printk (KERN_NOTICE "alloc_disk failure.\n");
     goto out_vfree;
 }
 dev->gd->major = sbull_major;
 dev->gd->first_minor = which*SBULL_MINORS;
 dev->gd->fops = &sbull_ops;
 dev->gd->queue = dev->queue;
 dev->gd->private_data= dev;
 snprintf(dev->gd->disk_name, 32, "sbull%c", which + 'a');
 set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));//使用KERNEL_SECTOR_SIZE本地常量,进行内核512字节扇区到实际使用的扇区大小转换
 
 add_disk(dev->gd);

        SBULL_MINORS是每个设备所支持的次设备号的数量,地一个设备名为 sbulla,第二个为sbullb.用户空间可以添加分区,第二个设备上的第三个分区可能是 /dev/sbullb3。


6、设置队列支持的扇区大小

        通知内核设备所支持的扇区大小,硬件扇区大小作为一个参数放在队列中,而不是在gendisk中。当分配好队列后就要马上调用下面函数:

        blk_queue_hardsect_size(dev->queue,  hardset_size);

        调用了上面的函数后,内核就会对我们的设备使用设定的硬件扇区大小,所有的I/O请求毒定位在硬件扇区的开始位置,并且每个请求的大小都将是扇区大小的整数倍。记住:内核总是认为扇区大小是512字节,因此必须将所有的扇区数进行转换。

7、实现操作函数:

打开设备函数:

 static int sbull_open(struct inode *inode,  struct file *filp)
 {
     struct sbull_dev  *dev = inode->i_bdev->bd_disk->private_data;
 
     del_timer_sync(&dev->timer);//移除定时器
     filp->private_data = dev;
     spin_lock(&dev->lock);
 
     if (!dev->users)
         check_disk_change(inode->i_bdev);//检查驱动器中的介质是否改变
     dev->users++;// 增加用户计数
     spin_unlock(&dev->lock);
     return 0;
 }

关闭设备函数:

 static int sbull_release(struct inode *inode,  struct file *filp)
 {
     struct sbull_dev  *dev = inode->i_bdev->bd_disk->private_data;
 
     spin_lock(&dev->lock);
     dev->users--;
 
     if (!dev->users){
         dev->timer.expires = jiffies + INVALIDATE_DELAY;//设置定时器
         add_timer(&dev->timer);
     }
 
     spin_unlock(&dev->lock);
     return 0;
 }

        其他的函数也是一样实现,和字符设备驱动的类似。这里就不写了,接下来看看核心部分,对于一个块设备驱动来说核心部分就是请求,几乎所有的重心都在请求函数;


8、处理请求操作

dev->queue = blk_queue_init(&sbull_request, &dev->lock);
 static void sbull_request(request_queue_t *q)
 {
     struct request *req;
 
     while((req = elv_next_request(q)) != NULL){//获取队列中第一个未完成的请求,没有则返回NULL。处理完后不删除该请求
         struct sbull_dev *dev = req->rq_disk->private_data;
         if (! blk_fs_request(req)){// 判断是否是一个文件系统请求,即是不是块设备请求
             printk(KERN_NOTICE "skip non-fs  request.\n");
             end_request(req, 0);
             continue;
         }
         //sbull_transfer()函数是真正的处理块设备请求函数
         sbull_transfer(dev, req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req));
         end_request(req, 1);
     }
 }
 
 void end_request(struct request* req, int succeeded);
 
// sector开始扇区的索引号,指的是512字节的扇区,如果是2048字节的扇区,则要sector/4再传递
// nsect 表示要传递多少个扇区; buffer 数据缓存的地址指针;write 表示数据传递的方向,即:read/write;
 static void sbull_transfer(struct sbull_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
 {
     unsigned long offset = sector*KERNEL_SECTOR_SIZE;
     unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
 
     if ((offset + nbytes) > dev->size){
         printk(KERN_NOTICE "Beyond-end  write (%ld %ld)\n", offset, nbytes);
         return;
     }
 
     if (write)
         memcpy(dev->data + offset, buffer, nbytes);
     else
         memcpy(buffer, dev->data + offset, nbytes);
 }

        转载地址: linux块设备驱动之实例

相关文章推荐

Linux块设备驱动程序原理

1.4  块设备驱动程序 1.4.1  Linux块设备驱动程序原理(1) 顾名思义,块设备驱动程序就是支持以块的方式进行读写的设备。块设备和字符设备最大的区别在于读写数据的基本单元不同。...

Linux块设备驱动总结

《Linux设备驱动程序》第十六章 块设备驱动程序读书笔记 简介 一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备 Linux内核视块设备为与字符设备相异的基本设备类型 Linu...

Linux设备驱动--块设备 之概念和框架

基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者...

《Linux设备驱动程序》——块设备驱动程序

块设备驱动程序

Linux 块设备驱动 (3)

本文将继续之前系列文章中的实验,围绕这个简单的 fio 测试,探究 Linux 块设备驱动和文件 IO 的运作机制。除非特别指明,本文中所有 Linux 内核源码引用都基于 4.6.0。其它内核版本可...
  • yayong
  • yayong
  • 2016年11月12日 16:26
  • 1406

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Linux设备驱动--块设备(一)之概念和框架

基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者...
  • myths_0
  • myths_0
  • 2012年03月22日 20:07
  • 1588

Linux块设备驱动(一) _驱动模型

http://www.cnblogs.com/xiaojiang1025/p/6500557.html 块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类...

Linux驱动编写(块设备驱动代码)

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】    按照ldd的说法,linux的设备驱动包括了char,block,net三种设备。cha...

linux 块设备-整理(一)

1. 基本概念:  linux设备驱动开发详解(宋宝华): 字符设备与块设备 I/O 操作的不同如下。 (1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位。大多数设备是字符设备,因...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux块设备驱动之实例
举报原因:
原因补充:

(最多只允许输入30个字)