Linux设备驱动--块设备(三)之程序设计

 块设备驱动注册与注销

块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:
int register_blkdev(unsigned int major, const char *name);


major 参数是块设备要使用的主设备号,name为设备名,它会在/proc/devices中被显示。 如果major为0,内核会自动分配一个新的主设备号register_blkdev()函数的返回值就是这个主设备号。如果返回1个负值,表明发生了一个错误。

与register_blkdev()对应的注销函数是unregister_blkdev(),其原型为:
int unregister_blkdev(unsigned int major, const char *name);
这里,传递给register_blkdev()的参数必须与传递给register_blkdev()的参数匹配,否则这个函数返回-EINVAL。

块设备的请求队列操作

标准的请求处理程序能排序请求,并合并相邻的请求,如果一个块设备希望使用标准的请求处理程序,那它必须调用函数blk_init_queue来初始化请求队列。当处理在队列上的请求时,必须持有队列自旋锁。初始化请求队列
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);


该函数的第1个参数是请求处理函数的指针,第2个参数是控制访问队列权限的自旋锁,这个函数会发生内存分配的行为,故它可能会失败,函数调用成
功时,它返回指向初始化请求队列的指针,否则,返回NULL。这个函数一般在块设备驱动的模块加载函数中调用。清除请求队列
void blk_cleanup_queue(request_queue_t * q);


这个函数完成将请求队列返回给系统的任务,一般在块设备驱动模块卸载函数中调用。

 

提取请求
struct request *elv_next_request(request_queue_t *queue);
上述函数用于返回下一个要处理的请求(由 I/O 调度器决定),如果没有请求则返回NULL。

去除请求
void blkdev_dequeue_request(struct request *req);
上述函数从队列中去除1个请求。如果驱动中同时从同一个队列中操作了多个请求,它必须以这样的方式将它们从队列中去除。

 

分配“请求队列”
request_queue_t *blk_alloc_queue(int gfp_mask);
对于FLASH、RAM盘等完全随机访问的非机械设备,并不需要进行复杂的I/O调度,这个时候,应该使用上述函数分配1个“请求队列”,并使用如下函数来绑定“请求队列”和“制造请求”函数。
void blk_queue_make_request(request_queue_t * q,
make_request_fn * mfn);

void blk_queue_hardsect_size(request_queue_t *queue,
unsigned short max);
该函数用于告知内核块设备硬件扇区的大小,所有由内核产生的请求都是这个大小的倍数并且被正确对界。但是,内核块设备层和驱动之间的通信还是以512字节扇区为单位进行。

 

步骤:

在块设备驱动的模块加载函数中通常需要完成如下工作:
① 分配、初始化请求队列,绑定请求队列和请求函数。
② 分配、初始化gendisk,给gendisk的major、fops、queue等成
员赋值,最后添加gendisk。
③ 注册块设备驱动。
在块设备驱动的模块卸载函数中通常需要与模块加载函数相反的工作:
① 清除请求队列。
② 删除gendisk和对gendisk的引用。
③ 删除对块设备的引用,注销块设备驱动。

总结:

块设备的I/O操作方式与字符设备存在较大的不同,因而引入了
request_queue、request、bio等一系列数据结构。在整个块设备的I/O操作中,贯穿于始终的就是“请求”,字符设备的I/O操作则是直接进行不绕弯,
块设备的I/O操作会排队和整合。

驱动的任务是处理请求,对请求的排队和整合由I/O调度算法解决,因此,块设备驱动的核心就是请求处理函数或“制造请求”函数。

尽管在块设备驱动中仍然存在block_device_operations结构体及其成员函数,但其不再包含读写一类的成员函数,而只是包含打开、释放及I/O控制等
与具体读写无关的函数。块设备驱动的结构相当复杂的,但幸运的是,块设备不像字符设备那么包罗万象,它通常就是存储设备,而且驱动的主体已经
由Linux内核提供,针对一个特定的硬件系统,驱动工程师所涉及到的工作往往只是编写少量的与硬件直接交互的代码。

#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>

#define MAXBUF 1024	


#define BLK_MAJOR 253

char blk_dev_name[]="blk_dev";
static char flash[1024*16];


int major;
spinlock_t lock;
struct gendisk *gd;



/*块设备数据传输*/
static void blk_transfer(unsigned long sector, unsigned long nsect, char *buffer, int write)
{
	int read = !write;
	if(read)
	{
		memcpy(buffer, flash+sector*512, nsect*512);
	}
	else
	{
		memcpy(flash+sector*512, buffer, nsect*512);
	}
}

/*块设备请求处理函数*/
static void blk_request_func(struct request_queue *q)
{
	struct request *req;
	while((req = elv_next_request(q)) != NULL)  
	{
		if(!blk_fs_request(req))
		{
			end_request(req, 0);
			continue;
		}
		
		blk_transfer(req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req));
		/*rq_data_dir从request获得数据传送的方向*/
		/*req->current_nr_sectors 在当前段中将完成的扇区数*/
		/*req->sector 将提交的下一个扇区*/
		end_request(req, 1);
	}
}

/*strcut block_device_operations*/
static  int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
{
       return -ENOTTY;
}

static int blk_open (struct block_device *dev , fmode_t no)
{
	printk("blk mount succeed\n");
	return 0;
}
static int blk_release(struct gendisk *gd , fmode_t no)
{
	printk("blk umount succeed\n");
	return 0;
}
struct block_device_operations blk_ops=
{
	.owner = THIS_MODULE,
	.open = blk_open,
	.release = blk_release,
    .ioctl = blk_ioctl,
};

//-----------------------------------------------

static int __init block_module_init(void)
{
	
	
	if(!register_blkdev(BLK_MAJOR, blk_dev_name)) //注册一个块设备
	{
		major = BLK_MAJOR;	
		printk("regiser blk dev succeed\n");
	}
	else
	{
		return -EBUSY;
	}
	gd = alloc_disk(1);  //分配一个gendisk,分区是一个
	spin_lock_init(&lock); //初始化一个自旋锁
	gd->major = major;
	gd->first_minor = 0;   //第一个次设备号
	gd->fops = &blk_ops;   //关联操作函数

	gd->queue = blk_init_queue(blk_request_func, &lock); //初始化请求队列并关联到gendisk

	snprintf(gd->disk_name, 32, "blk%c", 'a');  
	blk_queue_hardsect_size(gd->queue, 512);  //设置扇区大小512字节
	set_capacity(gd, 32);  //设置块设备大小 512*32=16K
	add_disk(gd);
	printk("gendisk init success!\n");
	return 0;
}
static void __exit block_module_exit(void)
{
	blk_cleanup_queue(gd->queue);
	del_gendisk(gd); 
	unregister_blkdev(BLK_MAJOR, blk_dev_name);
	printk("block module exit succeed!\n");
}

module_init(block_module_init);
module_exit(block_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("gec");
//------------------------------------------------------------------------------
  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux设备驱动程序是一种软件模块,它允许操作系统与硬件设备进行通信。设备驱动程序的设计需要遵循一定的规范和流程,包括以下几个步骤: 1. 确定设备类型:设备可以是字符设备块设备或网络设备等。每种设备类型都有不同的驱动程序接口和操作方法。 2. 编写设备驱动程序:设备驱动程序是一个内核模块,它包含了设备的初始化、读写操作、中断处理等函数。驱动程序需要遵循Linux内核的编程规范和API。 3. 注册设备驱动程序:驱动程序需要在内核中注册,以便操作系统能够识别和加载它。注册过程包括分配设备号、初始化设备结构体、注册字符设备块设备等。 4. 测试设备驱动程序:测试设备驱动程序需要使用一些工具和技术,例如ioctl命令、proc文件系统、sysfs文件系统等。测试过程需要验证设备的正确性、性能和稳定性。 以下是一个简单的字符设备驱动程序的例子: ```c #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #define DEVICE_NAME "mydevice" #define BUF_LEN 1024 static int Major; static char msg[BUF_LEN]; static char *msg_ptr; static int device_open(struct inode *inode, struct file *file) { msg_ptr = msg; try_module_get(THIS_MODULE); return 0; } static int device_release(struct inode *inode, struct file *file) { module_put(THIS_MODULE); return 0; } static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) { int bytes_read = 0; if (*msg_ptr == 0) return 0; while (length && *msg_ptr) { put_user(*(msg_ptr++), buffer++); length--; bytes_read++; } return bytes_read; } static ssize_t device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset) { int i; for (i = 0; i < length && i < BUF_LEN; i++) get_user(msg[i], buffer + i); msg_ptr = msg; return i; } static struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release }; static int __init init_module(void) { Major = register_chrdev(0, DEVICE_NAME, &fops); if (Major < 0) { printk(KERN_ALERT "Registering char device failed with %d\n", Major); return Major; } printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major); printk(KERN_INFO "the driver, create a dev file with\n"); printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major); return 0; } static void __exit cleanup_module(void) { unregister_chrdev(Major, DEVICE_NAME); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple example Linux module."); ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值