/*************************************************************************************************
mini2440 linux块设备驱动开发源代码:
**************************************************************************************************/
/*
insmod b_dev_tangjx.ko //动态挂载驱动模块
lsmod //查看是否多了个b_dev_tangjx 模块
ls /dev //查看一下/dev目录下是否多了个block_dev节点(节点会自动创建)
mkfs.ext3 /dev/block_dev //在虚拟磁盘上建立ext3文件系统
mount /dev/block_dev /mnt/test //挂载到/mnt/test目录下,然后去看看mnt/test下面是不是多了lost+found文件夹
终端敲入mount,看一下mount的记录,是不是ext3格式
对它进行读写:
#cp *.c /mnt/test
#ls /mnt/test 看内容是否写过去了
#cp su.c /
#ls /su.c 看是否读OK
玩够了之后就清除掉
umount /mnt/test
rmmod b_dev_tangjx.ko
(注意:要关闭这个终端新开一个终端,不然系统会认为该模块正被使用而不能卸载)
*/
// 上面步骤已经在pc机上测试OK ,但是在ARM平台运行时,ARM平台没有mkfs.ext3命令,所以没往下执行---SU
//使用了SBH-P289 无请求队列请求时使用的模板
#include <linux/string.h>
#include <linux/slab.h>
#include <asm/atomic.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/genhd.h>
#include <linux/buffer_head.h> /* for invalidate_bdev() */
#include <linux/backing-dev.h>
#include <linux/blkpg.h>
#include <linux/writeback.h>
#include <linux/version.h>
#include <asm/uaccess.h>
#include<linux/fs.h>
#include <linux/bio.h>
#include<linux/fs.h>
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/elevator.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/compiler.h>
/*宏定义*/
#define BLOCK_DEVICEMAJOR 0 //主设备号
#define BLOCK_DISKNAME "block_dev" //设备名称
#define BLOCK_BYTES (16*1024*1024) //存取块的大小
#define BLOCK_BLKDEV_MAXPARTITIONS 1 //(64) //总共最多的分区数
unsigned char block_data[BLOCK_BYTES]; //16m的大小,从内存中划分出来当前硬盘使用
static int block_devicemajor = BLOCK_DEVICEMAJOR; //主设备号
module_param( block_devicemajor, int, 0 );
struct block_driver
{
struct request_queue *block_queue; //块驱动的请求队列
struct gendisk *block_disk;
};/*自定义结构体*/
static struct block_driver *block_dev=NULL; //赋值NULL防止野指针
//这个函数会自动调用
static int block_blkdev_make_request(struct request_queue *q, struct bio *bio)
{
struct bio_vec *bvec;
int i;
void *dsk_mem;
// 检测访问请求是否超越了块设备限制 //bio是经过合并的数据块
if ((bio->bi_sector << 9) + bio->bi_size > BLOCK_BYTES)// bi_sector要传输的第一个扇区.bi_size要传输的字节数SBH_P285
{ //bi_sector << 9相当于当前扇区*512
printk(KERN_ERR "SIMP_BLKDEV_DISKNAME: bad request: block=%llu, count=%u\n", (unsigned long long)bio->bi_sector, bio->bi_size);
#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) ) //LINUX_VERSION_CODE记录着内核的版本
//bio_endio用于返回这个对bio请求的处理结果,在2.6.24之后的内核中,第一个参数是被处理的
//bio指针,第二个参数成功时为0,失败时为-ERRNO。
bio_endio(bio, 0, -EIO); //-EIO代表IO操作有出错
#else
bio_endio(bio, -EIO);
#endif
return 0;
}
//数组名block_data代表内存的首地址 + 扇区数*512
dsk_mem = block_data + (bio->bi_sector << 9); //指针指向要读写的第一个扇区首地址(挂载点)
bio_for_each_segment(bvec, bio, i) //遍历bio中的每个bvec ,把bio一个一个放到bvec中,i从0开始
{ //bio中的每个扇区
void *iovec_mem;
switch (bio_rw(bio)) {
case READ: //#define READ 0
case READA: //#define READA 2 /* read-ahead - don't block if no resources */
iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; // 映射一个物理页面,kmap定位下就知道它映射的位置(就知道挂载到哪个目录)
memcpy(iovec_mem, dsk_mem, bvec->bv_len); //把数据读出//把硬盘的东西dsk_mem读到iovec_mem目录挂载点
kunmap(bvec->bv_page); //数据已经复制过去,可以断开映射寻找挂载到哪个目录
break;
case WRITE: // #define WRITE 1 //数据从挂载点拷贝到硬盘中
iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
memcpy(dsk_mem, iovec_mem, bvec->bv_len); //把数据写入
kunmap(bvec->bv_page);
break;
default:
/*
// return READ, READA, or WRITE
#define bio_rw(bio) ((bio)->bi_rw & (RW_MASK | RWA_MASK))
*/
printk(KERN_ERR "SIMP_BLKDEV_DISKNAME: unknown value of bio_rw: %lu\n", bio_rw(bio));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
//bio_endio:设置块设备i/o停止
bio_endio(bio, 0, -EIO);//#define EIO 5 /* I/O error */
#else
bio_endio(bio, -EIO); //通知应用程出错
#endif
return 0;
}
dsk_mem += bvec->bv_len;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
bio_endio(bio, bio->bi_size, 0);
#else
bio_endio(bio, 0); //通知应用程已经完成
#endif
return 0;
}
//这就是我们的硬盘提供的操作接口
struct block_device_operations block_fops = {
.owner = THIS_MODULE,
};
static int __init block_init(void)
{
int ret;
//动态申请块设备号, 返回值就是主设备号,注册失败则返回负值。
block_devicemajor = register_blkdev( block_devicemajor, BLOCK_DISKNAME );//
if( block_devicemajor <= 0 )
{
printk( KERN_WARNING "GKDD-disk: unable to get major number\n" );
return -EBUSY;
}
printk("<0>" "block_devicemajor==>>%d\n" ,block_devicemajor); //打印出主设备号
//封装好的结构体指针 block_dev 分配内存
block_dev= kmalloc( sizeof( struct block_driver ), GFP_KERNEL );
if(block_dev == NULL)
{
goto err_kmalloc;
}
/*
* # define GFP_KERNEL 0
*/
/*分配一个请求队列*/
block_dev->block_queue = blk_alloc_queue(GFP_KERNEL);
if (!block_dev->block_queue)
{
ret = -ENOMEM;
printk("<0>" "block_queue::");
goto err_init_queue;
}
/*在队列中添加中断函数,把请求队列和请求函数绑定*/
blk_queue_make_request(block_dev->block_queue, block_blkdev_make_request);//请求合并好后会自动调用这个函数,请求函数相当于真正的读写函数
if (!block_dev->block_queue)
{
ret = -ENOMEM;
goto err_init_queue;
}
//安装对应的 block_dev 结构
block_dev->block_disk = alloc_disk(BLOCK_BLKDEV_MAXPARTITIONS);
if (!block_dev->block_disk)
{
ret = -ENOMEM;
goto err_alloc_disk;
}
//初始化gendisk
strcpy(block_dev->block_disk->disk_name, BLOCK_DISKNAME);
block_dev->block_disk->major = block_devicemajor;//注册块设备主设备号
block_dev->block_disk->first_minor = 0;//注册次设备号
block_dev->block_disk->fops = &block_fops; //内核提供给驱动层的函数接口
block_dev->block_disk->queue = block_dev->block_queue; //请求队列
block_dev->block_disk->private_data = block_dev; //保存我们的设备结构
/*
*容量,以扇区为单位
*以512-字节扇区来计. 传递扇区数目给
*/
set_capacity(block_dev->block_disk, BLOCK_BYTES>>9); //BLOCK_BYTES>>9 扇区的个数(BLOCK_BYTES/512,每个扇区大小512)
//做这一步前要确保一切准备工作都做好,一般放在最后一步
add_disk(block_dev->block_disk); //添加块设备加入到内核
printk( "<0>" "Module block init finish\n" );
return 0;
err_alloc_disk:
blk_cleanup_queue(block_dev->block_queue); //清除队列
//return ret;
err_init_queue:
kfree(block_dev);
//return ret;
err_kmalloc:
unregister_blkdev( block_devicemajor, BLOCK_DISKNAME );
return -ENOMEM;
}
static void __exit block_exit(void)
{
/*删除磁盘设备*/
del_gendisk(block_dev->block_disk); //删除分区,从内核中删除
put_disk(block_dev->block_disk); //释放扇区资源
blk_cleanup_queue(block_dev->block_queue);//清除块设备队列
unregister_blkdev( block_devicemajor, BLOCK_DISKNAME );//取消注册
kfree(block_dev);//释放块设备结构体内存空间
printk( "<0>" "Module block exit\n" );
}
module_init(block_init);
module_exit(block_exit);
//=====================================================================================
应用程序测试代码
//=====================================================================================
#include <linux/string.h>
#include <linux/slab.h>
#include <asm/atomic.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/buffer_head>
#include <linux/backing-dev.h>
#include <linux/blkpg.h>
#include <writeback.h>
#include <version.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/dlevator.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/compiler.h>
#define BLOCK_DEVICEMAJOR 0
#define BLOCK_DISKNAME "block_dev"
#define BLOCK_BYTES (16*1024*1024)
#define BLOCK_BLKDEV_MAXPARTITIONS 1
unsigned char block_data[BLOCK_BYTES];
static int block_devicemajor = BLOCK_DEVICEMAJOR;
module_param(block_devicemajor,int,0);
struct block_driver
{
struct request_queue *block_queue;
struct gendisk *block_disk;
};
static int block_blkdev_make_request(struct request_queue *q, struct bio *bio)
{
struct bio_vec *bvec;
int i;
void *dsk_mem;
if((bio->bi_sector <<9)+ bio->bi_size >BLOCK_BYTES)
{
printk(KERN_ERR "ERROR");
#if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
bio_endio(bio,0,-EIO);
#else
bio_endio(bio,-EIO);
#endif
}
dsk_mem=block_data+(bio->bi_sector<<9);
bio_for_each_segment(bvec,bio,i)
{
void *iovec_mem;
switch(bio_rw(bio))
{
case READ:
case READA:
iovec_mem = kmap(bvec->bv_page)+bvec->bv_offset;
memcpy(iovec_mem,dsk_mem,bvec->bv_len);
kunmap(bvec->bv_page);
break;
case WRITE:
iovec_mem=kmap(bvec->bv_page)+bvec->bv_offset;
memcpy(dsk_mem,iovec_mem,bvec->bv_len);
kunmap(bvec->bv_page);
break;
default:
printk(KERN_ERR "%lu\n",bio_rw(bio));
#if LINUX_VERSION_CODE <KERNEL_VERSION(25,6,24)
bio_endio(bio,0,-EIO);
#else
bio_endio(bio,0,-EIO);
#endif
return 0;
}
dsk_mem+=bvec->bv_len;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
bio_endio(bio,bio->bi_size,0);
#else
bio_endio(bio,0);
#endif
return 0;
}
struct block_device_operations block_fops={
.owner = THIS_MODULE,
};
static struct block_driver *block_dev = NULL;
static int __init block_init(void)
{
int ret;
block_devicemajor = register_blkdev(block_devicemajor,BLOCK_DISKNAME);
if(block_devicemajor<0)
{
printk(KERN_WARNING "unable to get major number\n");
return -EBUSY;
}
printk("<0>" "block_devicemajor==>>%d\n",block_devicemajor);
block_dev=kmalloc(sizeof(struct block_driver),GFP_KERNEL);
if(block_dev == NULL)
{
goto err_kmalloc;
}
block_dev->block_queue = blk_alloc_queue(GFP_KERNEL);
if(!block_dev->block_queue)
{
ret = -ENOMEM;
printk("<0>" "block_queue::");
goto err_init_queue;
}
blk_queue_make_request(block_dev->block_queue,block_blkdev_make_request);
if(!block_dev->block_queue)
{
ret= -ENOMEM;
goto err_init_queue;
}
block_dev->block_disk= alloc_disk(BLOCK_BLKDEV_MAXPARTITIONS);
if(!block_dev->block_disk)
{
ret =- ENOMEM;
goto err_alloc_disk;
}
strcpy(block_dev->block_disk->disk_name,BLOCK_DISKNAME);
block_dev->block_disk->major= block_devicemajor;
block_dev->block_disk->first_minor = 0;
block_dev->block_disk->fops=&block_fops;
block_dev->block_disk->queue= block_dev->block_queue;
block_dev->block_disk->private_data= block_dev;
set_capacity(block_dev->block_disk,BLOCK_BYTES>>9);
add_disk(block_dev->block_disk);
printk("<0>" "MOdule block init finish\n");
return 0;
err_alloc_disk:
blk_cleanup_queue(block_dev->block_queue);
err_init_queue:
kfree(block_dev);
err_kmalloc:
unregister_blkdev(block_devicemajor,BLOCK_DISKNAME);
return -ENOMEM;
}
static void __exit block_exit(void)
{
del_gendisk(block_dev->block_disk);
put_disk(block_dev->block_disk);
blk_cleanup_queue(block_dev->block_queue);
unregister_blkdev(block_devicemajor,BLOCK_DISKNAME);
kfree(block_dev);
printk("<0>" "Module block exit\n");
}
module_init(block_init);
module_exit(block_exit);
//=====================================================================================
Makefile
//=====================================================================================
#如果是PC机运行,则#make #如果是arm平台运行,则#make arch=arm
ifneq ($(arch),arm)
#CROSS=
#CC=$(CROSS)gcc
#LD=$(CROSS)ld
#AR=$(CROSS)ar #函数库打包,可创建静态库.a文档
#AS=$(CROSS)as #汇编程序
obj-m:=block.o
all:
make -C /usr/src/kernels/2.6.18-1.2798.fc6-i686 M=$(PWD) modules
else
CROSS=/usr/local/arm/4.3.2/bin/arm-linux-
CC=$(CROSS)gcc
LD=$(CROSS)ld
AR=$(CROSS)ar
AS=$(CROSS)as
obj-m:=block.o
globalmem_arm-objs:=block.o //同类型的多个设备
all:
#make -C /et10/linux-2.6.12 M=$(PWD) modules
make -C /mini2440/linux-2.6.32.2 M=$(PWD) modules
endif
clean:
@rm -rf *.o *.ko *.symvers *.mod.c