zoukankan      html  css  js  c++  java
  • linux块设备驱动之实例

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


    1、注冊:向内核注冊个块设备驱动。事实上就是用主设备号告诉内核这个代表块设备驱动

            sbull_major  =  register_blkdev(sbull_major, "sbull");
            if (0 >=  sbull_major){
                printk(KERN_WARNING "sbull:   unable  to  get  major  number!
    ");
                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.
    ");
         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.
    ");
         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.
    ");
                 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)
    ", offset, nbytes);
             return;
         }
     
         if (write)
             memcpy(dev->data + offset, buffer, nbytes);
         else
             memcpy(buffer, dev->data + offset, nbytes);
     }
    

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

  • 相关阅读:
    Array中使用异步函数遍历元素,Array循环同步执行
    vscode设置快捷键"h"快速生成html模板
    IOS(苹果手机)使用video播放HLS流,实现在内部播放及全屏播放(即非全屏和全屏播放)。
    FTP服务器与客户端的安装与配置
    移动端页面顶部滑动实现菜单的弹出与隐藏
    JS十大经典排序排序算法
    【bug】table重新加载数据,页面滚动条下沉到底部,记录scrollTop后将其恢复scrollTop出现闪烁
    寄生组合式继承
    扁平对象,转为树形对象
    使用CSS禁止textarea调整大小功能的方法
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7350762.html
Copyright © 2011-2022 走看看