您的位置:首页 > 运维架构 > Linux

块设备驱动向LINUX移植的常规步骤及INITRD的使用

2009-11-04 15:15 459 查看
这里提到的块设备泛指各种可以实现固定大小数据随机传输的设备,如FLASH、SD Card、RAMDISK等,块设备驱动移植的过程实际上是将硬件厂商提供的底层驱动代码与LINUX系统集成起来的过程。

在LINUX中,块设备能够以文件系统的形式为OS所访问,其大体架构如下:

LINUX Application
---------------------------------- system call
LINUX VFS
----------------------------------
File System (vfat/ext2/cramfs..)
----------------------------------
Block Device Driver
---------------------------------- <-- Our working scope
LLD from vendor
----------------------------------
Hardware

移植的常规过程为:

1. 利用module_init宏将设备的初始化函数mydev_init注册到kernel镜像的.init段。当然,还要利用module_exit宏注册去初始化函数mydev_cleanup。(ref: linux/init.h)

2. 填充初始化函数mydev_init,完成以下三项工作
2.1 使用register_blkdev注册块设备号和设备名。 (ref: drivers/block/genhd.c)
2.2 设置queue:主要是获取queue并设置request函数,设置sector大小等。 (ref: drivers/block/ll_rw_blk.c)
2.3 添加disk:add_disk。 (ref: drivers/block/genhd.c)
涉及到的数据结构包括:
a) struct request_queue (ref: linux/blkdev.h), 2.2中的queue结构
b) struct gendisk (ref: linux/genhd.h), 2.3中的disk结构,关注成员 ->
major/first_minor/minors <- 设备号;
fops指针 <- 块设备操作函数指针组
queue指针 <- 2.2中的queue
private_data指针 <- 存储与具体设备相关的数据结构
c) struct mydev_dev,即b)中的具体设备相关的数据结构,通常包含成员
size <- 设备中sector的数目
users <- 使用设备的用户数目
media_change <- media change标记
lock <- 自旋锁
queue 指针 <- 设备的struct request_queue指针
disk 指针 <- 设备的struct gendisk指针

3. LINUX块设备驱动的操作函数指针组
open: 设备打开时被调用,主要功能为初始化底层设备;或取底层设备信息
release: 设备关闭时被调用。
ioctl: 实现ioctl系统调用的函数,由于大量的标准请求为上层截取,一般块设备的ioctl比较短小。
media_changed: 内核调用该函数以检查用户是否更换了驱动器内的介质,如果用户更换了,那么返回一个非零的值。这是针对可移动介质的。
revalidate_disk: 当介质被更换时,调用该函数做出相应。

4. request函数
块设备驱动的数据传输主要由request函数来实现,因此,需要把硬件设备的lld集成到request函数中去。最简单的做法为:
void do_mydev_request(request_queue_t *q)
{
struct mydev_dev *dev;
struct request *req;
int result;

/* Process requests in the queue */
while((req = elv_next_request(q)) != NULL) {
dev = (struct mydev_dev*) req->rq_disk->private_data;
if (! blk_fs_request(req)) {
printk(KERNEL_NOTICE "Skip non-fs request/n");
end_request(req, 0);
continue;
}
result = mydev_transfer(dev, req->sector, req->current_nr_sectors, req->buffer, rq_data_dir(req));
if (result) {
printk(KERNEL_NOTICE "mydev_transfer error/n");
end_request(req, 0);
}
end_request(req, 1);
}
}
mydev_transfer是自定义的函数,它会调用设备的LLD进行sector为单位的读写:
static int mydev_transfer(struct sdmmc_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
{
int result = 0;

if ((sector + nsect) > dev->sectors) {
printk(KERN_NOTICE "[sdmmc_transfer]Error, beyond capa. limit(capa:%d, isec:%ld, nsec:%ld)/n",
dev->sectors, sector, nsect);
return -1;
}

spin_lock(&dev->lock);

if (write) {
result = LLD_write_sectors(sector,nsect,buffer);
}
else {
result = LLD_read_sectors(sector,nsect,buffer);
}

spin_unlock(&dev->lock);
return (result);
}

[其他:initrd的使用]
LINUX支持initrd,可以将内存中的ramdisk挂载为根文件系统。
要使用initrd,首先在config中激活CONFIG_BLK_DEV_RAM,实现ramdisk设备的支持;
其次,要在config中激活CONFIG_BLK_DEV_INITRD,实现initramfs的支持。
ramdisk可以做成多种linux支持的文件系统镜像,由boot loader加载到内存区域中,并在启动kernel的时候在cmdline中指定initrd_start和init_end。

如果完成了以上操作,LINUX在启动时会首先加载一个空白的/,然后将initrd_start处的ramdisk拷贝为/initrd.image,最后将这个文件输出到/dev/ram0中,则/dev/ram0就可以被当作根文件系统挂载了,因为/dev/ram0就是一个ramdisk设备。
(ref: populate_rootfs(void); initrd_load(void))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: