User Tools

Site Tools


tech:linux:initrd

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

这里提到的块设备泛指各种可以实现固定大小数据随机传输的设备,如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,完成以下三项工作

  1. 使用register_blkdev注册块设备号和设备名。 (ref: drivers/block/genhd.c)
  2. 设置queue:主要是获取queue并设置request函数,设置sector大小等。 (ref: drivers/block/ll_rw_blk.c)
  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))

tech/linux/initrd.txt · Last modified: 2014/11/10 08:22 (external edit)