linux 设备驱动(二)——块设备驱动

linux 设备驱动

  • 三、块设备驱动
    • 3.1. 块设备简介
    • 3.2. 块设备详细
      • 3.2.1 数据结构
      • 3.2.2 块设备初始化
      • 3.2.3 块设备request 操作
      • 3.2.4 块设备打开操作
      • 3.2.5 块设备释放操作
      • 3.2.6 块设备ioctl操作

三、块设备驱动

3.1. 块设备简介

块设备文件通常是指以块的方式写入的设备。如硬盘、光驱等。块设备驱动与字符设备驱动有很大差别。
块设备驱动程序描述符blk_dev_struct是定义如下:

<linux/blkdev.h>


struct blk_dev_struct {
request_queue_t request_queue;  //请求队列
queue_proc *queue;  //函数指针queue
void *date;  //保存一些私有数据
};

3.2. 块设备详细

块设备驱动编写包括注册和使用两类。其中request是请求队列,它是当内核安排一次数据传输时在列表中的请求队列。
linux 设备驱动(二)——块设备驱动_第1张图片

3.2.1 数据结构

块设备驱动程序绝大部分与设备无关,内核把相同的代码放在中简化驱动程序的代码,所以每个块设备驱动程序都必须包含这个头文件。
驱动程序要用的数据结构定义:

struct device_struct {
const char *name;
struct file_operations *chops;
}static struct device_struct blkdevs[MAX_BLKDEV]struct sbull_dev {
void **data;
int quantum;// the current quantum size
int qset;// the current array size
unsigned long size;
unsigned int access_key;// used by sbulluid and sbullpriv
unsigned int usage;// lock the device while using it
unsigned int new_msg;
struct sbull_dev *next;// next listitem
}

与字符设备驱动程序一样,块设备驱动程序也包含一个 file_operation 结构,其结构定义如下:

struct file_operation blk_fops = {
NULL//seek
block_read,//内核函数
block_write,//内核函数
NULL//readdir
NULL//poll
sbull_ioctl,// ioctl
NULL//mmap
sbull_open,//open
NULL//flush
sbull_release,//release
block_fsync,//内核函数
NULL//fasync
sbull_check_media_change,//check media change
NULL//revalidate
NULL//lock
}

从上面结构中可以看出,所有的块驱动程序都调用内核函数 :

block_read()
block_write()
block_fsync()

函数,所以在块设备驱动程序入口中不包含这些函数,只需包括:

 ioctl()
 open()
 release()

函数即可。

3.2.2 块设备初始化

块设备驱动要完成引导内核完成一定工作,还需要再内核编译时增加一些内容。块设备驱动程序初始化时,由驱动程序init() 完成。
块设备驱动程序初始化的工作主要包括:

· 检查硬件是否存在;
· 登记主设备号;
· 将 fops 结构的指针传递给内核;
· 利用 register_blkdev()函数对设备进行注册

register_blkdev注册详细如下:

if(register_blkdev(sbull_MAJOR,“sbull”,&sbull_fops)) {
printk(“Registering block device major:%d failed\n”,sbull_MAJOR)return-EIO;
}

将块设备驱动程序的数据容量传递给缓冲区:

#define sbull_HARDS_SIZE 512
#define sbull_BLOCK_SIZE 1024
static int sbull_hard = sbull_HARDS_SIZE;
static int sbull_soft = sbull_BLOCK_SIZE;
hardsect_size[sbull_MAJOR] = &sbull_hard;
blksize_size[sbull_MAJOR] = &sbull_soft;

在块设备驱动程序内核编译时,应把下列宏加到 blk.h 文件中:

#define MAJOR_NR sbull_MAJOR
#define DEVICE_NAME “sbull”
#define DEVICE_REQUEST sbull_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)

3.2.3 块设备request 操作

request操作涉及一个重要的数据结构:

struct request  {
    kdev_t rq_dev;
	int cmd; // 读或写
	int errors;
	unsigned long sector;
	char *buffer;
	struct request *next;
};

设备的读写操作都是由request()函数完成。所有的读写请求都存储在 request 结构的链表中。request()函数利用 CURRENT 宏检查当前的请求,如下:

#define CURRENT (blk_dev[MAJOR_NR].current_request)
接下来看一看 sbull_request 的具体使用:
extern struct request *CURRENT;
void sbull_request(void) {
unsigned long offset,total;
Begin:
INIT_REQUEST:
offset = CURRENT -> sector * sbull_hard;
total = CURRENT -> current_nr_sectors * sbull_hard;
/*超出设备的边界*/
if(total + offset > sbull_size * 1024) {
/*请求错误*/
end_request(0)goto Begin;
}
if(CURRENT -> cmd == READ) {
memcpy(CURRENT -> buffer,sbull_storage + offset,total)}
else if(CURRENT -> cmd == WRITE) {
memcpy(sbull_storage + offset,CURRENT -> buffer,total)}
else {
end_request(0)}
/*成功*/
end_request(1)/*当请求做完时让 INIT_REQUEST 返回*/
goto Begin;
}

假定队列中至少有一个请求,request()函数现在应处理队列中的第一个请求,当处理完
请求后,request()函数将调用 end_request()函数。如果成功地完成了读写操作,那么应该用参
数值 1 调用 end_request()函数;如果读写操作不成功,那么以参数值 0 调用 end_request()函数。
如果队列中还有其他请求,那么将 CURRENT 指针设为指向下一个请求。执行 end_request()
函数后,request()函数回到循环的起点,对下一个请求重复上面的处理过程。

3.2.4 块设备打开操作

打开块设备流程图和代码如下:
linux 设备驱动(二)——块设备驱动_第2张图片

int sbull_open(struct inode *inode,struct file *filp) {
int num = MINOR(inode -> i_rdev)if(num >= sbull -> size)
return ENODEV;
sbull -> size = sbull -> size + num;
if(!sbull -> usage) {
check_disk_change(inode -> i_rdev)if(!*(sbull -> data))
return -ENOMEM;
}
sbull -> usage++;
MOD_INC_USE_COUNT;
return 0}

3.2.5 块设备释放操作

释放块设备流程图和代码如下:
linux 设备驱动(二)——块设备驱动_第3张图片

void sbull_release(struct inode *inode,struct file *filp) {
sbull -> size = sbull -> size + MINOR(inode -> i_rdev);
sbull -> usage--;
MOD_DEC_USE_COUNT;
printk("This blkdev is in release!\n")return 0}

3.2.6 块设备ioctl操作

ioctl块设备流程图和代码如下:
linux 设备驱动(二)——块设备驱动_第4张图片

#include 
#include 
int sbull_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,
unsigned long arg) {
int err;
struct hd_geometry *geo = (struct hd_geometry *)arg;
PDEBUG(“ioctl 0x%x 0x%lx\n”,cmd,arg)switch(cmd) {
case BLKGETSIZE:
/ *返回设备大小*/
if(!arg)
return –EINVAL; // NULL pointer:not valid
err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long))if(err)
return err;
put_user(1024*sbull_sizes[MINOR(inode -> i_rdev)/sbull_hardsects
[MINOR(inode -> i_rdev)](long*)arg]return 0case BLKFLSBUF: // flush
if(!suser())
return –EACCES; // only root
fsync_dev(inode -> i_rdev)return 0case BLKRRPART: // re-read partition table: can’t do it
return –EINVAL;
RO_IOCTLS(inode -> i_rdev,arg)// the default RO operations,宏 RO_IOCTLS(kdev_t dev,unsigned long where)
在 blk.h 中定义
}
return –EINVAL; // unknown command
}

感谢阅读,祝君成功!
-by aiziyou

你可能感兴趣的:(linux驱动,驱动程序)