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

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

 

2、定义设备结构体:

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

 

3、初始化设备结构体:

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

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

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

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

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

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

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

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

blk_queue_hardsect_size(dev->queue,  hardset_size);

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

7、实现操作函数:

打开设备函数:

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

关闭设备函数:

  1. static int sbull_release(struct inode *inode,  struct file *filp)
  2. {
  3. struct sbull_dev  *dev = inode->i_bdev->bd_disk->private_data;
  4. spin_lock(&dev->lock);
  5. dev->users--;
  6. if (!dev->users){
  7. dev->timer.expires = jiffies + INVALIDATE_DELAY;//设置定时器
  8. add_timer(&dev->timer);
  9. }
  10. spin_unlock(&dev->lock);
  11. return 0;
  12. }

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

8、处理请求操作

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

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

http://blog.csdn.net/yuzhihui_no1/article/details/46808947

linux块设备驱动之实例的更多相关文章

  1. Linux块设备驱动详解

    <机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个 ...

  2. Linux块设备驱动(二) _MTD驱动及其用户空间编程

    MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...

  3. linux块设备驱动

    块设备驱动程序<1>.块设备和字符设备的区别1.读取数据的单元不同,块设备读写数据的基本单元是块,字符设备的基本单元是字节.2.块设备可以随机访问,字符设备只能顺序访问. 块设备的访问:当 ...

  4. linux块设备驱动---相关结构体(转)

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  5. linux块设备驱动---概念与框架(转)

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

  6. Linux 块设备驱动 (一)

    1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...

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

    块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...

  8. Linux块设备驱动_WDS

    推荐书:<Linux内核源代码情景分析> 1.字符设备驱动和使用中等待某一事件的方法①查询方式②休眠唤醒,但是这种没有超时时间③poll机制,在休眠唤醒基础上加一个超时时间④异步通知,异步 ...

  9. linux 块设备驱动 (三)块设备驱动开发

    一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...

随机推荐

  1. iOS开发中的权限

    权限分类 联网权限 相册权限 相机.麦克风权限 定位权限 推送权限 通讯录权限 日历.备忘录权限 联网权限 引入头文件 @import CoreTelephony; 应用启动后,检测应用中是否有联网权 ...

  2. Quartz2D 编程指南(四)位图与图像遮罩、CoreGraphics 绘制 Layer

    概览 图形上下文 路径 颜色与颜色空间 变换 图案 阴影 渐变 透明层 Quartz 2D 中的数据管理 位图与图像遮罩 CoreGraphics 绘制 Layer 位图与图像遮罩 简介 位图与图像遮 ...

  3. java new Date()得到的时间和系统时间不一样

    造成这种问题的原因是:操作系统时区跟JVM的时区不一致. [root@paas244 ~]# timedatectl Local time: Thu 2016-12-29 15:35:44 CST U ...

  4. css重置reset.css

    body, h1, h2, h3, h4, h5, h6, hr, p, blockquote,dl, dt, dd, ul, ol, li,pre,form, fieldset, legend, b ...

  5. pip install 安装python-requests

    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple python-requests

  6. 数据库触发器inserted和deleted详解

    create trigger updateDeleteTime on user for update as begin   update user set UpdateTime=(getdate()) ...

  7. sp_helpdb

    语法 sp_helpdb [ [ @dbname= ] 'name' ] 参数 [@dbname=] 'name' 是要为其提供信息的数据库名称.name 的数据类型为 sysname,无默认值.如果 ...

  8. APP测试要点

    APP测试的时候,建议让开发打好包APK和IPA安装包,测试人员自己安装应用,进行测试.在测试过程中需要注意的测试点如下: 1.安装和卸载 ●应用是否可以在IOS不同系统版本或android不同系统版 ...

  9. c3p0连接池]

    <c3p0-config> <!-- 默认配置 --> <default-config> <property name="jdbcUrl" ...

  10. Jquery,jquery-cookie.js 做的点击记住用户名和密码!

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...