您的位置:首页 > 运维架构 > Linux

linux块设备驱动之实例

2016-12-28 19:46 330 查看
整体过程:

1注册register_blkdev;

2定义设备结构体以及初始化结构图sbull_dev;

3创建设备请求队列 dev->queue = blk_init_queue(sbull_request, &dev->lock);

4分配、初始化及安装相应的gendisk结构dev->gd = alloc_disk(SBULL_MINORS)。dev = ,,;

5设置队列支持的扇区大小blk_queue_hardsect_size(dev->queue, hardset_size);

6实现操作函数,打开,关闭

7处理请求操作

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

[cpp] view plain copy

sbull_major = register_blkdev(sbull_major, "sbull");

if (0 >= sbull_major){

printk(KERN_WARNING "sbull: unable to get major number!\n");

return -EBUSY;

}

2、定义设备结构体:

[cpp] view plain copy

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、初始化设备结构体:

[cpp] view plain copy

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结构:

[cpp] view plain copy

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、实现操作函数:

打开设备函数:

[cpp] view plain copy

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;

}

关闭设备函数:

[cpp] view plain copy

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、处理请求操作

[cpp] view plain copy

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块设备驱动之实例
http://blog.csdn.net/yuzhihui_no1/article/details/46808947
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: