驱动:
//---mydisk.c 实现最简功能
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/vmalloc.h>
MODULE_LICENSE("Dual BSD/GPL");
/*DTS
mydisk {
compatible = "xdisk";
parts = <4>;
kschedule_enable = <1>;
gpios = <&gpx2 7 0>;
};
*/
//#define MYDISK_MINORS 4 //硬盘的分区数
#define MYDISK_HEADS 4
#define MYDISK_SECTORS 16 // 磁盘的扇区数
#define MYDISK_CYLINDERS 256 // 柱面数(多个磁盘的磁道叠加形成柱面)
#define MYDISK_SECTOR_SIZE 512 // 磁盘扇区大小
#define MYDISK_SECTOR_TOTAL (MYDISK_HEADS * MYDISK_SECTORS * MYDISK_CYLINDERS)
#define MYDISK_SIZE (MYDISK_SECTOR_TOTAL * MYDISK_SECTOR_SIZE)
struct disk{
int gpio; // 磁盘读写指示灯
int mydisk_major;
int MYDISK_MINORS;
spinlock_t mylock;
int kschedule_enable;
char mydisk_name[20];
struct gendisk *mydisk;
char data[MYDISK_SIZE];
}MYDISK;
static int mydisk_open(struct block_device *bdev, fmode_t mode)
{
gpio_request(MYDISK.gpio, "mydisk gpio");
gpio_direction_output(MYDISK.gpio, 0);
return 0;
}
static void mydisk_release(struct gendisk* gd, fmode_t mode)
{
return ;
}
static int mydisk_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long arg)
{
return 0;
}
//获取磁盘几何信息
static int mydisk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
geo->cylinders = MYDISK_CYLINDERS;
geo->heads = MYDISK_HEADS;
geo->sectors = MYDISK_SECTORS;
geo->start = 0;
return 0;
}
// 处理块层发来的磁盘操作请求队列
static void mydisk_request_handler(struct request_queue *q)
{
int status = 0;
volatile int i = 0,j = 0,k = 0, t = 0;
struct request *req;
struct bio *bio;
struct bio_vec bvec;
struct bvec_iter iter;
sector_t sector;
unsigned char *buffer;
unsigned long offset;
unsigned long nbytes;
struct mydisk_dev *bdev;
printk("enter: %s!\n", __func__);
gpio_set_value(MYDISK.gpio, 1);
bdev = q->queuedata;
req = blk_fetch_request(q);
/* 逻辑结构(多叉树)
1. request queue: O -----> O -----> O -----> O -----> O -----> O -----> O -----> O
/ | \ / | \ / | \ / | \ / | \ / | \ / | \ / | \
/ | \ / | \ / | \ / | \ / | \ / | \ / | \ / | \
2. bio O O O O O O O O O O O O O O O O O O O O O O O O
/|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\
3. bv_page o o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o oo o o o o o o
*/
/* 第一层遍历,遍历每一个request */
while (req != NULL) {
i++;
printk("request_queue count: %d\n", i);
/* 第二层遍历,遍历每一个bio */
__rq_for_each_bio(bio, req) {
j++;
printk("bio count: %d\n", j);
sector = bio->bi_iter.bi_sector;
if (bio_end_sector(bio) > get_capacity(MYDISK.mydisk)) {
status = -EIO;
goto out;
}
/* 对bvec中的每个页提供服务 */
bio_for_each_segment(bvec, bio, iter) {
k++;
printk("segment(page) count: %d\n", k);
buffer = kmap_atomic(bvec.bv_page);
offset = sector * MYDISK_SECTOR_SIZE;
nbytes = bvec.bv_len;
// 读写采用内存模拟
if (bio_data_dir(bio) == WRITE)
memcpy(MYDISK.data + offset, buffer + bvec.bv_offset, nbytes);
else
memcpy(buffer + bvec.bv_offset, MYDISK.data + offset, nbytes);
for(t = 0; t < nbytes; t++) {
// printk("buffer: %c", (buffer+ bvec.bv_offset)[t]);
}
// printk("\n");
kunmap_atomic(buffer);
sector += nbytes >> 9;
}
status = 0;
}
gpio_set_value(MYDISK.gpio, 0);
out:
if (!__blk_end_request_cur(req, status))
req = blk_fetch_request(q);
}
}
static struct block_device_operations mydisk_fops = {
.owner = THIS_MODULE,
.open = mydisk_open,
.release = mydisk_release,
.ioctl = mydisk_ioctl,
.getgeo = mydisk_getgeo,
};
// 自定义的处理块层发来的磁盘请求队列
void my_make_request_func(struct request_queue *q, struct bio *bio)
{
unsigned char *buffer;
unsigned long offset;
unsigned long nbytes;
struct bio_vec bvec;
struct bvec_iter iter;
// printk("bio %s !\n", (bio_data_dir(bio) == WRITE)?"write":"read");
sector_t sector = bio->bi_iter.bi_sector;
gpio_set_value(MYDISK.gpio, 1);
bio_for_each_segment(bvec, bio, iter) {
buffer = kmap_atomic(bvec.bv_page);
offset = sector * MYDISK_SECTOR_SIZE;
nbytes = bvec.bv_len;
if (bio_data_dir(bio) == WRITE) // 解析读写动作,并执行(这里时内存模拟),实际中应该使用具体的块设备读写操作接口
// 注意这里使用的数据源地址为上面映射得到的虚拟地址
memcpy(MYDISK.data + offset, buffer + bvec.bv_offset, nbytes);
else
memcpy(buffer + bvec.bv_offset, MYDISK.data + offset, nbytes);
kunmap_atomic(buffer);
sector += nbytes >> 9;
}
gpio_set_value(MYDISK.gpio, 0);
return bio_endio(bio, 0);
}
static int disk_probe(struct platform_device *pdev)
{
printk("match ok\n");
of_property_read_u32(pdev->dev.of_node, "parts", &MYDISK.MYDISK_MINORS);
of_property_read_u32(pdev->dev.of_node, "kschedule_enable", &MYDISK.kschedule_enable);
MYDISK.gpio = of_get_gpio(pdev->dev.of_node, 0);
printk("parts: %d\n", MYDISK.MYDISK_MINORS);
printk("schedule: %d\n", MYDISK.kschedule_enable);
printk("gpio: %d\n", MYDISK.gpio);
// 1. 申请主设备号,如果参数为0,表示自动分配一个主设备号
MYDISK.mydisk_major = register_blkdev(MYDISK.mydisk_major, MYDISK.mydisk_name);
if (MYDISK.mydisk_major <= 0) {
printk(KERN_WARNING "mydisk: unable to get major number\n");
return -EBUSY;
}
// 2. 申请块设备对象
MYDISK.mydisk = alloc_disk(MYDISK.MYDISK_MINORS);
if (!MYDISK.mydisk) {
printk (KERN_NOTICE "alloc_disk failure\n");
return -1;
}
spin_lock_init(&MYDISK.mylock);
if(MYDISK.kschedule_enable) {// need schedule help(使用内核提供的块层的块设备IO操作调度算法)
MYDISK.mydisk->queue = blk_init_queue(mydisk_request_handler, &MYDISK.mylock);
}
else {
// blk_queue_logical_block_size(bdev->queue, MYDISK_SECTOR_SIZE);
MYDISK.mydisk->queue = blk_alloc_queue(GFP_KERNEL);
// 使用自己的请求调度方法
blk_queue_make_request(MYDISK.mydisk->queue, my_make_request_func);
}
// 对象成员构造
MYDISK.mydisk->major = MYDISK.mydisk_major;
MYDISK.mydisk->first_minor = 0;
MYDISK.mydisk->fops = &mydisk_fops;
// 磁盘设备名
snprintf(MYDISK.mydisk->disk_name, 32, "mydisk%c", 'a');
set_capacity(MYDISK.mydisk, MYDISK_SECTOR_TOTAL);
// disk设备注册到kernel
add_disk(MYDISK.mydisk);
return 0;
}
static int disk_remove(struct platform_device *pdev)
{
del_gendisk(MYDISK.mydisk);
// put_disk(mydisk);
blk_cleanup_queue(MYDISK.mydisk->queue);
unregister_blkdev(MYDISK.mydisk_major, "mydisk");
return 0;
}
#ifdef CONFIG_OF
struct of_device_id disk_table[] = {
{.compatible = "xdisk"},
{}
};
#endif
struct platform_driver disk_driver = {
.probe = disk_probe,
.remove = disk_remove,
.driver = {
.name = "xdisk",
.of_match_table = of_match_ptr(disk_table)
}
};
module_platform_driver(disk_driver);
测试:
1.分区
fdisk /dev/mydiska
p->n->w
2.格式化
mkfs.ext2 /dev/mydiska1
3.挂载
mount /dev/mydisk1 /mnt/
4.测试
cd /mnt/
touch test