Linux驱动开发:块设备驱动

目录

1、块设备简介

2、块设备驱动API

2.1 block_device

2.1.1 register_blkdev:注册块设备

2.1.2 unregister_blkdev:注销块设备

2.2 gendisk

2.2.1 gendisk 结构体

2.2.2 alloc_disk:申请gensisk

2.2.3 put_disk:释放disk

2.2.4 add_disk:将已初始化好的gendisk指针添加到内核 

2.2.5 del_gendisk:删除gendisk

2.2.6 set_capacity:设置磁盘容量

2.3 request_queue

2.3.1 request

2.3.2 request_queue

2.3.3 bio

2.3.4 bio_vec 

2.3.5 blk_mq_init_sq_queue

2.3.6 blk_cleanup_queue

3、驱动程序


1、块设备简介

1、系统中能够随机访问固定大小(1block 512byte)数据片的设备被称之为块设备。块设备文件一般都是以安装文件系统的方式使用,这也是块设备通常的访问方式。块设备的方式访问方式是随机的。

2、块设备中小的可寻址单位是扇区,扇区大小一般是2的整数倍。常见的大小是512字节。扇区的大小是块设备的物理属性,扇区是所有块设备的基本单元,块设备无法对比扇区更小的单位进行寻址和操作。

3、块是文件系统的一种抽象,只能基于块来访问文件系统。物理磁盘寻址是按照扇区的级别进行的,内核访问的所有磁盘操作又都是按照块进行的。扇区是设备的小可寻址单位,所以快不能比扇区还小,只能数倍于扇区大小。

4、内核对块大小的要求是:必须是扇区大小的整数倍,并且小于页面的大小,所以块的大小通常是512字节、1K或者4K。

5、块设备的结构:

磁头:一个磁盘有多少个面就有多少个磁头

磁道:在一个磁头上可以有很多环,这些环就叫做磁道

扇区:磁道上访问数据的最小的单位就是扇区,一个扇区的大小就是512字节

块设备的能存储的数据=磁头*磁道*扇区*512

2、块设备驱动API

2.1 block_device

2.1.1 register_blkdev:注册块设备

int register_blkdev(unsigned int major, const char *name)
/*
功能:注册块设备,申请设备设备驱动的主设备号
参数:
    @major : 0:自动申请
              >0 :静态指定
    @name  :名字  cat /proc/devices
返回值:	
        major=0 ;成功返回主设备号,失败返回错误码
        major>0 :成功返回0 ,失败返回错误码
*/

2.1.2 unregister_blkdev:注销块设备

void unregister_blkdev(unsigned int major, const char *name)
/*
功能:注销块设备,释放设备号
参数:
    @major:主设备号
    @name:名字
*/

2.2 gendisk

2.2.1 gendisk 结构体

1.gendisk的结构体对象
struct gendisk {   
    int major;                        //块设备的主设备号
    int first_minor;                  //起始的次设备号
    int minors;                       //设备的个数,分区的个数
    char disk_name[DISK_NAME_LEN];    //磁盘的名字
    struct disk_part_tbl  *part_tbl;  //磁盘的分区表的首地址
    struct hd_struct part0;           //part0分区的描述
    const struct block_device_operations *fops; //块设备的操作方法结构体
    struct request_queue *queue;      //队列
    void *private_data;               //私有数据
};

分区的结构体
struct hd_struct {
    sector_t start_sect; //起始的扇区号
    sector_t nr_sects;   //扇区的个数
    int  partno;        //分区号
};

//块设备的操作方法结构体
struct block_device_operations {
    int (*open) (struct block_device *, fmode_t);
    int (*release) (struct gendisk *, fmode_t);
    int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
    int (*getgeo)(struct block_device *, struct hd_geometry *);	
    //设置磁盘的磁头,磁道,扇区的个数的。hd_geometry
}	

2.2.2 alloc_disk:申请gensisk

struct gendisk *alloc_disk(int minors)
/*
功能:分配gendisk的内存,然后完成必要的初始化
参数:
    @minors:分区的个数
返回值:成功返回分配到的内存的首地址,失败返回NULL
*/

2.2.3 put_disk:释放disk

void put_disk(struct gendisk *disk)
/*
功能:释放申请的disk内存
参数:
    @disk:申请的gendisk 结构体
*/

2.2.4 add_disk:将已初始化好的gendisk指针添加到内核 

void add_disk(struct gendisk *disk)
/*
功能:使用alloc_disk申请到 gendisk以后系统还不能使用,
      必须使用 add_disk函数将申请到的gendisk添加到内核中,
参数:
    @disk:申请的gendisk 结构体
*/

2.2.5 del_gendisk:删除gendisk

void del_gendisk(struct gendisk *disk)
/*
功能:注销磁盘设备
参数:
    @disk:申请的gendisk 结构体
*/

2.2.6 set_capacity:设置磁盘容量

void set_capacity(struct gendisk *disk, sector_t size)
/*
功能:设置磁盘的容量
参数:
    @disk:申请的gendisk 结构体
    @size:磁盘的容量
*/

2.3 request_queue

2.3.1 request

struct  request
{
    struct list_head queuelist; // 请求对象中的链表元素
    struct request_queue *q;	// 指向存放当前请求的请求队列
    unsigned int __data_len;	// 当前请求要求数据传输的总的数据量 
    sector_t __sector;          // 当前请求要求数据传输的块设备的起始扇区 
    struct bio *bio;		    // bio对象所携带的信息转存至请求对象中
    struct bio *biotail;	    // bio链表
};

2.3.2 request_queue

struct  request_queue 
{
    //双向链表数据结构,将所有加入到队列的IO请求组建成一个双向链表
    struct  list_head  queue_head; 
    struct list_head    requeue_list; //request队列
    spinlock_t      requeue_lock;     //队列自旋锁
    unsigned long     nr_requests;    // 最大的请求数量 
    unsigned long     queue_flags;    //当前请求队列的状QUEUE_FLAG_STOPPED
    …
};

2.3.3 bio

struct bio {	   
	struct bio *bi_next; 	       // 指向当前bio的下一个对象*
	unsigned long  bi_flags; 	   // 状态、命令等  
	unsigned long bi_rw; 	       // 表示READ/WRITE
	struct block_device *bi_bdev;  // 与请求相关联的块设备对象指针   
	unsigned short bi_vcnt;	       // bi_io_vec数组中元素个数 
	unsigned short bi_idx;	       // 当前处理的bi_io_vec数组元素索引
	unsigned int bi_size;	       // 本次传输需要传输的数据总量,byte(扇区大小整数倍)  
	struct bio_vec *bi_io_vec; //指向一个IO向量的数组,数组中的内各元素对应一个物理页的page对象
};

2.3.4 bio_vec 

struct bio_vec {		
    struct page  *bv_page; //指向用于数据传输的页面所对应的struct page对象
    unsigned int bv_len;   //表示当前要传输的数据大小		
    unsigned int bv_offset;//表示数据在页面内的偏移量	
};

2.3.5 blk_mq_init_sq_queue

struct request_queue *blk_mq_init_sq_queue(struct blk_mq_tag_set *set,
                                           const struct blk_mq_ops *ops,
                                           unsigned int queue_depth,
                                           unsigned int set_flags)
/*
功能:用于在给定队列深度的情况下使用mq ops设置队列的助手,以及通过mq ops标志传递的助手
参数:
    @set:被初始化的tag对象,tag被上层使用,里面包含硬件队列的个数,队列的操作方法结构体,标志位等
          使用时直接初始化一个set放进去就好,是给上层用的
    @ops:放入到tag中的操作方法结构体
    @queue_depth:中指定支持的队列深度
    @set_flags:将tag中队列的处理标志位,例如BLK_MQ_F_SHOULD_MERGE,BLK_MQ_F_BLOCKING等
返回值:
	成功返回队列指针,失败返回错误码指针 
*/

2.3.6 blk_cleanup_queue

void blk_cleanup_queue(struct request_queue *q)
/*
功能:该函数完成将请求队列返回给系统的任务,一般在块设备驱动模块卸载函数中使用。
参数:
    @q:请求队列
*/

3、驱动程序

#define BLKSIZE (1 * 1024 * 1024) // 1M
#define DISKNAME "mydisk"
struct gendisk* mydisk;
int major = 0;
struct request_queue* q;
struct blk_mq_tag_set set;
char* disk_addr = NULL; //磁盘的首地址

blk_status_t mydisk_queue_rq(struct blk_mq_hw_ctx* hctx,
    const struct blk_mq_queue_data* bd)
{
    blk_status_t status = BLK_STS_OK;
    struct request* rq = bd->rq;
    loff_t pos = blk_rq_pos(rq) << 9;
    struct bio_vec bvec;
    struct req_iterator iter;
    //开始处理队列
    blk_mq_start_request(rq);

    //循环从requet中获取bio_vec
    rq_for_each_segment(bvec, rq, iter)
    {
        unsigned long b_len = bvec.bv_len;
		//获取用户相关的线性地址
        void* b_buf = page_address(bvec.bv_page) + bvec.bv_offset;

        //校验越界
        if ((pos + b_len) > BLKSIZE)
            b_len = (unsigned long)(BLKSIZE - pos);

        if (rq_data_dir(rq)) // WRITE
            memcpy(disk_addr + pos, b_buf, b_len);
        else // READ
            memcpy(b_buf,disk_addr + pos, b_len);

        pos += b_len;
    }

    //队列处理结束
    blk_mq_end_request(rq, status);
    return status;
}

struct blk_mq_ops mqops = {
    .queue_rq = mydisk_queue_rq,
};

int mydisk_open(struct block_device* blkdev, fmode_t mode)
{
    return 0;
}

int mydisk_getgeo(struct block_device* blkdev, struct hd_geometry* hd)
{
    hd->heads = 4;
    hd->cylinders = 16;
    hd->sectors = BLKSIZE / hd->heads / hd->cylinders / 512;
    return 0;
}

void mydisk_close(struct gendisk* disk, fmode_t mode)
{
}

struct block_device_operations fops = {
    .open = mydisk_open,
    .release = mydisk_close,
    .getgeo = mydisk_getgeo,
};

static int __init mydisk_init(void)
{
    // 1.分配gendisk
    mydisk = alloc_disk(4);

    // 2.对象初始化
    major = register_blkdev(0, DISKNAME);

    //设置磁盘的容量
    set_capacity(mydisk, BLKSIZE >> 9);

    //初始化IO请求队列
    q = blk_mq_init_sq_queue(&set, &mqops, 2, BLK_MQ_F_SHOULD_MERGE);

    mydisk->major = major;
    mydisk->first_minor = 0;
    //这是一个数组,数组只有在定义的时候才能赋初值
    strcpy(mydisk->disk_name, DISKNAME);
    mydisk->fops = &fops;
    mydisk->queue = q;

    // 3.分配1M的内存当成硬盘使用
    disk_addr = vmalloc(BLKSIZE);

    // 4.注册
    add_disk(mydisk);

    return 0;
}
static void __exit mydisk_exit(void)
{
    del_gendisk(mydisk);
    vfree(disk_addr);
    blk_cleanup_queue(q);
    unregister_blkdev(major, DISKNAME);
    put_disk(mydisk);
}
module_init(mydisk_init);
module_exit(mydisk_exit);

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
static void battery_module_capture_process(battery_capture_type_t capture_type) { device_batt_info_t *p_batt = (device_batt_info_t *)store_driver_get(STORE_DATA_ID_BATT_INFO); device_mode_factory_t *p_mode = (device_mode_factory_t *)store_driver_get(STORE_DATA_ID_MODE_STATUS); if(capture_type == BATTERY_CAPTURE_TYPE_UP) { //启动充电动画 ui_module_set_menu_class(UI_MODULE_MENU_BAT); g_battery_module_mgr.b_low_voltage = false; g_battery_module_mgr.capacity.init_time_count = my_fw_timer_hw_get_cnt(); g_battery_module_mgr.capacity.before_charge_persentage = p_batt->battery_persentage; battery_module_tick_check_process(); if(p_mode->f1 == SYSTEM_MODE_WORK) { } else { my_fw_sched_event_put(FACTORY_MODULE_EVENT_CHARGE_DEFAULT,0,NULL,MID_LVL_EVT); } } else { //去除充电动画 ui_module_set_menu_class(UI_MODULE_MENU_NONE); g_battery_module_mgr.capacity.b_discharge_delay = true; g_battery_module_mgr.factory_data.init_discharge_count = my_fw_timer_hw_get_cnt(); g_battery_module_mgr.capacity.charge_time = 0; g_battery_module_mgr.factory_data.discharge_time = 0; g_battery_module_mgr.factory_data.b_discharge_init_per= false; g_battery_module_mgr.capacity.after_charge_persentage = p_batt->battery_persentage; my_fw_tm_start(REMOTE_MODULE_EVENT_TIMEOUT,0,NULL,ROWER_TIMEOUT); //my_fw_sched_event_put(UI_MODULE_EVENT_MENU_DISCHARGE_DEFAULT,0,NULL,MID_LVL_EVT); if(p_mode->f1 == SYSTEM_MODE_WORK) { my_fw_sched_event_put(UI_MODULE_EVENT_MENU_DISCHARGE_DEFAULT,0,NULL,MID_LVL_EVT); } else { my_fw_sched_event_put(FACTORY_MODULE_EVENT_DISCHARGE_DEFAULT,0,NULL,MID_LVL_EVT); } } }
06-06

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值