通過記憶體模擬硬碟實現一個簡單的塊裝置驅動
阿新 • • 發佈:2019-01-05
本文的主要工作是通過硬碟來模擬記憶體,按照塊裝置驅動程式設計的框架實現一個簡單的塊裝置驅動程式。
一、前期的準備工作
1、基本開發環境
Linux核心版本:Linux-3.4.10
開發板 : JZ2440(ARM9)
2、塊裝置驅動的一般開發步驟
a、分配一個gendisk的結構體變數
b、設定這個結構體變數,
b1、分配設定一個佇列,通過它來為塊裝置提供讀寫能力
b2、設定gendisk結構體的其他成員
c、註冊這個gendisk結構體的變數
二、編寫塊裝置驅動程式
通過在記憶體中分配2MB的空間來模擬硬碟,實現簡單的塊裝置的讀寫、格式化、掛接等操作。為了驅動編寫的方便,定義了這樣一個全域性的結構體,並通過這個結構體定義了一個全域性變數,具體實現如下:
1、分配一個gendisk結構體變數,具體實現如下:/* 定義一個yl_ramdisk_t的結構體,封裝驅動程式使用的各種資料 */ struct yl_ramdisk_t { int major; // 主裝置號 unsigned char *ramdisk_buffer; // 在記憶體中分配的緩衝區的儲存區 struct gendisk *gendisk; // 定義一個gendisk結構體變數指標變數 struct request_queue *queue; // 定義一個請求佇列的結構體指標變數 }; /* 定義一個yl_ramdisk結構體的全域性變數 */ static struct yl_ramdisk_t yl_ramdisk;
/* 1、分配一個gendisk結構體變數 */
yl_ramdisk.gendisk = alloc_disk(8); /* 分割槽數 + 1 = minors*/
if (!yl_ramdisk.gendisk)
{
printk("alloc_disk error!\n");
return -ENOMEM;
}
2、設定這個分配的gendisk結構體變數的成員,具體如下:
2.1 分配一個佇列,主要是為了給塊裝置提供讀寫能力
這裡面需要提供一個函式來實現具體的塊裝置的讀寫操作:do_yl_ramdisk_request()函式,它的具體實現如下:/* 2.1 分配一個佇列,提供讀寫能力 */ yl_ramdisk.queue = blk_init_queue(do_yl_ramdisk_request, &ramdisk_lock); if (!yl_ramdisk.queue) { put_disk(yl_ramdisk.gendisk); printk("blk_init_queue error!\n"); return -ENOMEM; }
/* 定義佇列處理函式 */
static void do_yl_ramdisk_request(struct request_queue *q)
{
struct request *req;
/* 從求情佇列裡面獲得一個請求 */
req = blk_fetch_request(q);
while (req)
{
unsigned long offset = blk_rq_pos(req) << 9; // 獲取ramdisk的偏移值
unsigned long len = blk_rq_cur_bytes(req); // 獲取傳輸資料的大小
int err = 0;
/* 根據讀寫來決定資料傳輸方向 */
if (rq_data_dir(req) == READ)
memcpy(req->buffer, (char *)(yl_ramdisk.ramdisk_buffer + offset), len);
else
memcpy((char *)(yl_ramdisk.ramdisk_buffer + offset), req->buffer, len);
/* 判斷是否是佇列尾部,如果不是再次獲得一個請求 */
if (!__blk_end_request_cur(req, err))
req = blk_fetch_request(q);
}
}
2.2 設定gendisk其他相關的屬性、成員,具體如下:/* 2.2 設定其他相關屬性 */
yl_ramdisk.major = register_blkdev(0, "yl_ramdisk"); // 註冊塊裝置,獲取主裝置號
yl_ramdisk.gendisk->major = yl_ramdisk.major; // 設定主裝置號
yl_ramdisk.gendisk->first_minor = 0; // 設定次裝置號的起始為0
yl_ramdisk.gendisk->queue = yl_ramdisk.queue; // 設定佇列
yl_ramdisk.gendisk->fops = &yl_ramdisk_fops; // 塊裝置操作函式集合
sprintf(yl_ramdisk.gendisk->disk_name, YL_DEVICE_NAME); // 設定名字
set_capacity(yl_ramdisk.gendisk, YL_RAMDISK_SIZE / 512); // 設定ramdisk的大小
這裡面實現了一個塊裝置操作的函式結合的成員:yl_ramdisk_fops,它的主要功能就是實現對塊裝置的操作,我們這裡主要用它實現模擬硬碟時磁頭、柱面、扇區等的大小,它的具體實現如下:/* 模擬機械硬碟,為其設定磁頭、柱面、扇區等的大小 */
static int yl_ramdisk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/* 磁碟大小 = heads*cylinders*sectors*512 */
geo->heads = 4;
geo->cylinders = 32;
geo->sectors = YL_RAMDISK_SIZE/geo->heads/geo->cylinders/512;
return 0;
}
/* 定義塊裝置的操作函式結構體變數 */
static const struct block_device_operations yl_ramdisk_fops = {
.owner = THIS_MODULE,
.getgeo = yl_ramdisk_getgeo,
};
3、硬體相關的操作,主要是在記憶體中分配2MB的記憶體空間,為模擬硬碟做準備,具體實現如下:/* 3、硬體相關的操作 */
yl_ramdisk.ramdisk_buffer = kzalloc(YL_RAMDISK_SIZE, GFP_KERNEL);
if (!yl_ramdisk.ramdisk_buffer) {
unregister_blkdev(yl_ramdisk.major, YL_DEVICE_NAME);
blk_cleanup_queue(yl_ramdisk.queue);
put_disk(yl_ramdisk.gendisk);
printk("kzalloc error!\n");
return -ENOMEM;
}
4、註冊這個gendisk結構體變數/* 4、註冊這個gendisk結構體變數 */
add_disk(yl_ramdisk.gendisk);
完整的程式程式碼實現如下所示:
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/gfp.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
/* 定義ramdisk的大小為2MB */
#define YL_RAMDISK_SIZE ((2) * (1024) * (1024))
/* 定義ramdisk塊裝置的名稱 */
#define YL_DEVICE_NAME "yl_ramdisk"
/* 定義一個yl_ramdisk_t的結構體,封裝驅動程式使用的各種資料 */
struct yl_ramdisk_t
{
int major; // 主裝置號
unsigned char *ramdisk_buffer; // 在記憶體中分配的緩衝區的儲存區
struct gendisk *gendisk; // 定義一個gendisk結構體變數指標變數
struct request_queue *queue; // 定義一個請求佇列的結構體指標變數
};
/* 定義一個yl_ramdisk結構體的全域性變數 */
static struct yl_ramdisk_t yl_ramdisk;
/* 模擬機械硬碟,為其設定磁頭、柱面、扇區等的大小 */
static int yl_ramdisk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/* 磁碟大小 = heads*cylinders*sectors*512 */
geo->heads = 4;
geo->cylinders = 32;
geo->sectors = YL_RAMDISK_SIZE/geo->heads/geo->cylinders/512;
return 0;
}
/* 定義塊裝置的操作函式結構體變數 */
static const struct block_device_operations yl_ramdisk_fops = {
.owner = THIS_MODULE,
.getgeo = yl_ramdisk_getgeo,
};
/* 定義一個自旋鎖,用於分配一個佇列 */
static DEFINE_SPINLOCK(ramdisk_lock);
/* 定義佇列處理函式 */
static void do_yl_ramdisk_request(struct request_queue *q)
{
struct request *req;
/* 從求情佇列裡面獲得一個請求 */
req = blk_fetch_request(q);
while (req)
{
unsigned long offset = blk_rq_pos(req) << 9; // 獲取ramdisk的偏移值
unsigned long len = blk_rq_cur_bytes(req); // 獲取傳輸資料的大小
int err = 0;
/* 根據讀寫來決定資料傳輸方向 */
if (rq_data_dir(req) == READ)
memcpy(req->buffer, (char *)(yl_ramdisk.ramdisk_buffer + offset), len);
else
memcpy((char *)(yl_ramdisk.ramdisk_buffer + offset), req->buffer, len);
/* 判斷是否是佇列尾部,如果不是再次獲得一個請求 */
if (!__blk_end_request_cur(req, err))
req = blk_fetch_request(q);
}
}
/* 定義入口函式 */
static int __init yl_ramdisk_init(void)
{
/* 1、分配一個gendisk結構體變數 */
yl_ramdisk.gendisk = alloc_disk(8); /* 分割槽數 + 1 = minors*/
if (!yl_ramdisk.gendisk)
{
printk("alloc_disk error!\n");
return -ENOMEM;
}
/* 2、設定分配的gendisk結構體變數 */
/* 2.1 分配一個佇列,提供讀寫能力 */
yl_ramdisk.queue = blk_init_queue(do_yl_ramdisk_request, &ramdisk_lock);
if (!yl_ramdisk.queue)
{
put_disk(yl_ramdisk.gendisk);
printk("blk_init_queue error!\n");
return -ENOMEM;
}
/* 2.2 設定其他相關屬性 */
yl_ramdisk.major = register_blkdev(0, "yl_ramdisk"); // 註冊塊裝置,獲取主裝置號
yl_ramdisk.gendisk->major = yl_ramdisk.major; // 設定主裝置號
yl_ramdisk.gendisk->first_minor = 0; // 設定次裝置號的起始為0
yl_ramdisk.gendisk->queue = yl_ramdisk.queue; // 設定佇列
yl_ramdisk.gendisk->fops = &yl_ramdisk_fops; // 塊裝置操作函式集合
sprintf(yl_ramdisk.gendisk->disk_name, YL_DEVICE_NAME); // 設定名字
set_capacity(yl_ramdisk.gendisk, YL_RAMDISK_SIZE / 512); // 設定ramdisk的大小
/* 3、硬體相關的操作 */
yl_ramdisk.ramdisk_buffer = kzalloc(YL_RAMDISK_SIZE, GFP_KERNEL);
if (!yl_ramdisk.ramdisk_buffer) {
unregister_blkdev(yl_ramdisk.major, YL_DEVICE_NAME);
blk_cleanup_queue(yl_ramdisk.queue);
put_disk(yl_ramdisk.gendisk);
printk("kzalloc error!\n");
return -ENOMEM;
}
/* 4、註冊這個gendisk結構體變數 */
add_disk(yl_ramdisk.gendisk);
return 0;
}
/* 定義出口函式 */
static void __exit yl_ramdisk_exit(void)
{
/* 將入口函式分配的資源依次釋放掉 */
del_gendisk(yl_ramdisk.gendisk);
put_disk(yl_ramdisk.gendisk);
blk_cleanup_queue(yl_ramdisk.queue);
unregister_blkdev(yl_ramdisk.major, YL_DEVICE_NAME);
kfree(yl_ramdisk.ramdisk_buffer);
}
module_init(yl_ramdisk_init);
module_exit(yl_ramdisk_exit);
MODULE_LICENSE("GPL");