设为首页 加入收藏

TOP

Linux驱动开发之块设备初入门(五)
2016-12-12 08:15:24 】 浏览:632
Tags:Linux 驱动 开发 设备 入门
blkdev(xxx_MAJOR, "xxx");


}


2.块设备驱动的打开与释放


块设备驱动的open()和release()函数不是必须的,一个简单的块设备驱动可以不提供open()和release()函数.


块设备驱动的open()函数和字符设备驱动的open()和类似,都以相关inode和file结构体指针作为参数,当一个结点引用一个块设备时,inode->i_bdev->bd_disk包含一个指向关联gendisk的结构体的指针.因此类似字符设备,可将gendisk的private_data赋给file的private_data,private_data同样最好是指向描述该设备的设备结构体xxx_dev的指针.如下面的代码:


static int xxx_open(struct inode* inode, struct file* file){


struct xxx_dev* dev = inode->i_bdev->db_disk->private_data;


file->private_data = dev;


...


return 0;


}


3.块设备驱动的ioctl


块设备可以包含一个ioctl()函数,以提供对该设备的IO控制,实际上搞成的块设备层代码处理了绝大多数ioctl(),因此具体的块设备驱动中,通常不在需要实现很多ioctl()命令.下面的代码中只实现一个命令HDIO_GETGEO,用于获得磁盘的几何信息(geometry,指CHS,即Cylinder, Head, Sector/Track).


static int xxx_ioctl(struct inode* inode, struct file* file,\


unsigned int cmd, unsigned long arg){


long size;


struct hd_geometry geo;


struct xxx_dev* dev = file->private_data;


switch(cmd){


case HDIO_GETGEO:


size = dev->size * (hardsect_size / KERNEL_SECTOR_SIZE);


geo.cylinders = (size & ~0x3f) >> 6;


geo.heads = 4;


geo.sectors = 16;


if(copy_to_user((void __user*)arg, &geo, sizeof(geo)){


return -EFAULT;


}


return 0;


}


return -ENOTTY;//未知命令


}


4.块设备驱动的I/O请求


☆ 使用请求队列


块设备驱动请求函数的原型为:


void request(request_queue_t* q);


这个函数不能由驱动自己调用,只有当内核认为是时候让驱动处理对设备的读写等操作时,它才会调用这个函数.请求函数可以在没有完成请求队列中的所有请求的情况下返回,甚至它一个请求不完成都可以返回.但对大部分设备而言,一般会在请求函数中处理完所有请求后才返回.


static void xxx_request(request_queue_t* q){


struct request* req;


//elv_next_request()用于获取队列中第一个未完成的请求


//end_request()会将请求从请求队列中剥离


while((req = elv_next_request(q)) != NULL){


struct xxx_dev* dev = req->rq_disk->private_data;


if(!blk_fs_request(req)){//如果不是文件系统请求,直接清除,调用end_request().


printk(KERN_NOTICE "Skip non-fs request\n");


end_request(req, 0);//通知请求处理失败.第二个参数0代表请求失败.


continue;


}


xxx_transfer(dev, req->sector, req->current_nr_sectors, req->buffer,\


rq_data_dir(req));//处理这个请求.


end_request(req, 1);//通知成功完成这个请求.1,表示请求成功.


}


}


static void xxx_transfer(struct xxx_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)


write_dev(offset, buffer, nbytes);//向设备写nbytes个字节的数据.


else


read_dev(offset, buffer, nbytes);//从设备读取nbytes个字节的数据.


}


下面是end_that_request_first()的源码和分析


//end_request()源码清单


void end_request(struct request* req, int uptodate){


//当设备完成一个IO请求的部分或全部扇区传输后,必须告知块设备层.end_that_request_first


//原型为:int end_that_request_first(struct request* req, int success, int count);


//此函数高数块设备层,已经完成count各扇区的传送.返回表示所有扇区传送完毕.


if(!end_that_request_first(req, uptodate, req->hard_cur_sectors)){


//add_disk_randomness()作用是使用块IO请求的定时来给系统的随机数池贡献熵,它不影响


//块设备,但仅当磁盘的操作时间是真正随机的时候,才调用它.


add_disk_randomness(req->rq_disk);


blkdev_dequeue_request(req);//清除此请求.


end_that_request_last(req);//通知等待此请求的对象,此请求已经完成


首页 上一页 2 3 4 5 6 下一页 尾页 5/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Nand Flash驱动程序编写 下一篇在Linux下的中断方式读取按键驱动..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目