本文基于linux2.6.38的版本,调用了内核块设备驱动的接口函数。 linux2.6.38与之前的的linux2.6.22,块设备驱动程序接口函数有比较大的区别,故编写驱动程序时,要注意参考所使用的linux的版本中,对应的设备驱动文件。不同的linux内核版本,驱动函数接口可能差别比较大。
本文通过使用内存中模拟1M的磁盘,对该磁盘进行设备驱动程序的编写,熟悉块设备驱动程序的框架以及驱动程序中对块设备读与写直接的调度。交叉编译后加载到开发板中,进行实验。
实验驱动代码如下:
myramblock.c:
-
#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/blkpg.h>
-
#include <linux/delay.h>
-
#include <linux/io.h>
-
#include <asm/system.h>
-
#include <asm/uaccess.h>
-
#include <asm/dma.h>
-
-
#define RAMBLOCK_SIZE (1024*1024)
-
static struct gendisk *my_ramblock_disk;
-
static struct request_queue *my_ramblock_queue;
-
static DEFINE_SPINLOCK(my_ramblock_lock);
-
static int major;
-
static unsigned char *my_ramblock_buf;
-
static int my_ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
-
{
-
/* 容量=heads*cylinders*sectors*512 */
-
geo->heads = 2; //磁头数
-
geo->cylinders = 32; //柱面数
-
geo->sectors = RAMBLOCK_SIZE/2/32/512; //扇区数
-
return 0;
-
}
-
-
static void do_my_ramblock_request(struct request *q)
-
{
-
struct request *req;
-
static int r_cnt=0; //实验用,打印出驱动读与写的调度方法
-
static int w_cnt=0;
-
-
req = blk_fetch_request(q);
-
while (req)
-
{
-
unsigned long start = blk_rq_pos(req) *512;
-
unsigned long len = blk_rq_cur_bytes(req);
-
if(rq_data_dir(req)==READ)
-
{
-
memcpy(req->buffer, my_ramblock_buf+start, len); //读操作,
-
printk("do_my_ramblock-request read %d times\n",r_cnt++);
-
}
-
else
-
{
-
memcpy( my_ramblock_buf+start,req->buffer, len); //写操作
-
printk("do_my_ramblock request write %d times \n",w_cnt++);
-
}
-
-
// end_request(req,1);
-
if(!__blk_end_request_cur(req, 0))
-
{
-
req = blk_fetch_request(q);
-
}
-
}
-
}
-
-
static const struct block_device_operations my_ramblock_fops =
-
{
-
.owner = THIS_MODULE,
-
.getgeo = my_ramblock_getgeo, //获取磁盘几何属性
-
};
-
-
-
static int my_ramblock_init(void)
-
{
-
/*分配一个 gendisk 结构体*/
-
my_ramblock_disk=alloc_disk(10);//次设备个数 ,分区个数 +1
-
-
major=register_blkdev(0, "my_ramblock");
-
-
//分配设置请求队列,提供读写能力
-
my_ramblock_queue=blk_init_queue(do_my_ramblock_request,&my_ramblock_lock);
-
//设置硬盘属性
-
my_ramblock_disk->major=major;
-
my_ramblock_disk->first_minor=0;
-
my_ramblock_disk->fops=&my_ramblock_fops;
-
sprintf(my_ramblock_disk->disk_name, "my_ramblcok");
-
my_ramblock_disk->queue=my_ramblock_queue;
-
set_capacity(my_ramblock_disk, RAMBLOCK_SIZE/512);
-
/* 硬件相关操作 */
-
my_ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
-
-
add_disk(my_ramblock_disk);
-
return 0;
-
-
-
}
-
-
-
static void my_ramblock_exit(void)
-
{
-
unregister_blkdev(major, "my_ramblock");
-
del_gendisk(my_ramblock_disk);
-
put_disk(my_ramblock_disk);
-
blk_cleanup_queue(my_ramblock_queue);
-
kfree(my_ramblock_buf);
-
}
-
-
module_init(my_ramblock_init);
-
module_exit(my_ramblock_exit);
-
MODULE_LICENSE("GPL");
编译的Makefile:
-
KERN_DIR = /home/linux-2.6.38
-
all:
-
make -C $(KERN_DIR) M=`pwd` modules
-
clean:
-
make -C $(KERN_DIR) M=`pwd` modules clean
-
rm -rf modules.order
-
obj-m += myramblock.o
KERN_DIR 目录中必须是已经交叉编译过的linux源代码位置
编译后,得到myramblock.ko文件。
对驱动程序的测试:(测试过程中注意观察读写的顺序以及时刻)
-
-
1:使用insmod加载驱动程序。
-
2:加载成功后,可在/dev目录下,看到加载的设备myramblock.使用 mkdosfs /dev/myramblock,初始化虚拟磁盘。
-
3: 把磁盘挂接到tmp目录下,mount /dev/myramblock /tmp
-
4:在tmp目录中,创建文件并写入任意文字,或者复制文件到该目录中。
-
5:卸载挂接目录 umount /tmp
-
6:把磁盘里面的东西创建成一个映像文件 cat /dev/myramblock > myramblock.bin
-
7:把myramblock.bin 的文件在PC机上,或者开发板中 当成一个磁盘挂载 sudo mount -o loop myramblock.bin /mnt
-
8:在mnt中,可以看到第4步中给磁盘写入的文件。表明实验成功。
可以对该磁盘使用fdisk工具进行分区。具体分区方法,参见fdisk说明。
阅读(4773) | 评论(1) | 转发(0) |