分类: 嵌入式
2015-06-09 12:46:49
块设备驱动程序
15年6月1日16:26:12
程序如下所示:
1
2
#include
3
#include
4
#include
5
#include
6
#include
7
#include
8
#include
9
#include
10
#include
11
#include
12
#include
13
#include
14
#include
15
#include
16
#include
17
#include
18
19
#include
20
#include
21
#include
22
23 static struct gendisk *ramblock_disk;
24 static request_queue_t *ramblock_queue;
25 static int major;
26
27 static DEFINE_SPINLOCK(ramblock_lock);
28
29 static struct block_device_operations ramblock_fops = {
30 .owner = THIS_MODULE,
31 };
32
33 #define RAMBLOCK_SIZE (1024*1024)
34 static unsigned char *ramblock_buf;
35
36 static void do_ramblock_queue(request_queue_t *q)
37 {
38 static int cnt = 0;
39 struct request *req;
40
41 //printk("do_ramblock_queue: %d\n", ++cnt);
42
43 while ((req = elv_next_request(q)) != NULL) {
44 unsigned long offset = req->sector * 512;
45 unsigned long len = req->current_nr_sectors * 512;
46
47 if (rq_data_dir(req) == READ)
48 {
49 memcpy(req->buffer, ramblock_buf + offset, len);
50 }
51 else
52 {
53 memcpy(ramblock_buf + offset, req->buffer, len);
54 }
55 end_request(req, 1);
56 }
57 }
58
59 static int ramblock_init(void)
60 {
61 major = register_blkdev(0, "ramblock");
62
63 ramblock_disk = alloc_disk(16);
64
65 ramblock_queue = blk_init_queue(do_ramblock_queue, &ramblock_lock);
66 ramblock_disk->queue = ramblock_queue;
67
68 ramblock_disk->major = major;
69 ramblock_disk->first_minor = 0;
70 sprintf(ramblock_disk->disk_name, "ramblock");
71 ramblock_disk->fops = &ramblock_fops;
72 set_capacity(ramblock_disk, RAMBLOCK_SIZE/512);
73
74 ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
75
76 add_disk(ramblock_disk);
77
78 return 0;
79 }
80
81 static void ramblock_exit(void)
82 {
83 unregister_blkdev(major, "ramblock");
84 del_gendisk(ramblock_disk);
85 put_disk(ramblock_disk);
86 blk_cleanup_queue(ramblock_queue);
87
88 kfree(ramblock_buf);
89 }
90
91 module_init(ramblock_init);
92 module_exit(ramblock_exit);
93
94 MODULE_LICENSE("GPL");
95
(一)gendisk 结构体
1.1 块设备驱动的核心就是gendisk(通用磁盘)结构体,在内核中使用gendisk来表示一个独立的 磁盘设备(或分区),定义如下:
struct gendisk {
int major; /* major number of driver */
int first_minor;
int minors; /* maximum number of minors, =1 for disks that can't be partitioned. */
char disk_name[32]; /* name of major driver */
struct hd_struct **part; /* [indexed by minor] */
int part_uevent_suppress;
struct block_device_operations *fops; //块设备操作函数
struct request_queue *queue; /*I/O 请求队列*/
void *private_data;
sector_t capacity; /* 容量,以512字节为一个扇区 */
int flags;
struct device *driverfs_dev;
struct kobject kobj;
struct kobject *holder_dir;
struct kobject *slave_dir;
struct timer_rand_state *random;
int policy;
atomic_t sync_io; /* RAID */
unsigned long stamp;
int in_flight;
#ifdef CONFIG_SMP
struct disk_stats *dkstats;
#else
struct disk_stats dkstats;
#endif
struct work_struct async_notify;
};
1.2 这个结构体里面 major表示磁盘的主设备号,同一个磁盘的分区共享一个主设备号,而次设备号不同。 minors表示次设备号的最大个数,它同样表示这一个磁盘可以分成(minors-1)个分区,因为minors=1的话,表示的就是这一个磁盘,不能被分区。struct block_device_operations *fops;表示块设备操作集合,struct request_queue *queue;表示内核用来管理这个设备的I/O请求队列的指针。
(1)如果要使用这个gendisk结构体,首先需要动态分配它,使用struct gendisk *alloc_disk(int minors)来动态分配它。
(2)进行一些初始化,包括指定主设备号,名称,容量,设备操作函数,队列等东西。
(3)初始化完成以后,系统还不能使用这个gendisk结构体,需要调用void add_disk(struct gendisk *disk)来注册这个磁盘设备。注意,add_disk()函数的调用必须发生在驱动程序的初始化工作完成并能响应磁盘的请求之后。
(4)如果不再需要使用一个磁盘,应当使用void del_gendisk(struct gendisk *disk)函数来释放卸载 gendisk。
1.3 下面介绍 struct block_device_operations *fops结构体:
struct block_device_operations {
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);
long (*compat_ioctl) (struct file *, unsigned, unsigned long);
int (*direct_access) (struct block_device *, sector_t, unsigned long *);
int (*media_changed) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
int (*getgeo)(struct block_device *, struct hd_geometry *);
struct module *owner;
};
块设备的 block_device_operations结构体功能退化了,open和release函数就是在当设备打开关闭时调用他们。另一个重要的是 getgeo函数,该函数根据驱动器的几何信息填充一个hd_geometry结构体,这个结构体包含磁头,扇区,柱面等信息。
另外,可以看到,在这个结构体里面,没有函数负责读和写数据,在块设备的I/O子系统中,这些操作是由request函数处理的。
(二)请求处理
2.1 如果说块设备驱动程序的核心是gendisk结构体的话,那么gendisk结构体的核心就是request函数(请求处理函数)。这个函数的原型如下所示:
static void request(request_queue_t *queue)。
2.2 每个设备都有一个请求队列,当请求队列生成的时候,就将该队列和request处理函数绑定在一起。例如本例中,request处理函数就是do_ramblock_queue函数,通过 ramblock_queue = blk_init_queue(do_ramblock_queue, &ramblock_lock);将他们两者绑定起来。
请求队列拥有request_queue和request_queue_t两种结构类型,其实他们两者是相同的。
一个请求队列就是一个动态的数据结构,必须由块设备的I/O子系统创建,通过下面这个函数来完成请求队列的创建和初始化工作:
request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)。
函数的参数是处理这个队列的request处理函数指针和控制访问队列权限的自旋锁。自旋锁通过下面这个函数来声明:static DEFINE_SPINLOCK(ramblock_lock);
为了把请求队列返回给系统(通常在卸载模块的时候),需要调用blk_cleanup_queue函数:
void blk_cleanup_queue(request_queue_t * q)。
2.3 只有很少的几个函数负责处理队列中的请求。在调用这些函数前,必须拥有队列锁。
返回队列中下一个要处理的请求的函数是elv_next_request函数。函数原型如下:
struct request *elv_next_request(request_queue_t *q)。
(三)下面总结写块设备驱动程序的步骤:
首先,注册块设备驱动,使用register_blkdev函数。
创建请求队列,并且通过ramblock_queue = blk_init_queue(do_ramblock_queue, &ramblock_lock);函数将请求队列和请求处理函数绑定联系在一起。
创建一个通用磁盘(gendisk)结构体,并且分配,初始化它,包括:主设备号,第一个次设备号,设备名称,对应的块操作函数和对应的请求队列,以及容量等,初始化完毕后,最后调用add_disk来注册这个磁盘设备。
在出口函数中做相反的操作。
编写请求处理函数。
(四)测试,在挂载的时候,出现如下图所示的情况,没有解决。同时,可能是由于我用busybox1.7构建的根文件系统中,没有mkfs等工具,没法完成测试,所以等以后从新用高点等级的busybox构建来完成测试。