现在的位置: 首页 > 综合 > 正文

linux-0.11内核 硬盘驱动程序

2012年10月29日 ⁄ 综合 ⁄ 共 2811字 ⁄ 字号 评论关闭

一、概述

首先要说明的是内核每次读写的数据量以一个逻辑块为单位,即1024B,而块设备控制器则是以扇区(512B)为单位。

对硬盘的读写操作是通过中断处理程序进行的,使用读写请求项等待队列来顺序缓冲一次读写多个逻辑块的操作。进程读

硬盘上的一个逻辑块时向缓冲区管理程序提出申请,然后进入睡眠状态。缓冲区管理程序检查缓冲区以前是否已经读取过

这块数据,如有直接将对应的缓冲区块头指针返回给进程并唤醒它,否则,调用ll_rw_block()向相应的设备驱动程序发出

一个读数据块的操作请求。该函数创建一个请求结构项,插入队列,插入时采用电梯算法(至今还没读懂电梯算法)。如对应

设备的请求项队列空,则设备不忙,立即向该设备控制器发出读数据命令。当设备控制器将数据读入到指定的缓冲块中后,就

会发出中断请求信号,并调用相应的读命令后处理函数,处理继续读扇区操作或者结束本次请求项的过程。(引自赵炯老师)

二、数据结构

块设备项

struct blk_dev_struct {
 void (*request_fn)(void);//请求项操作的函数指针
 struct request * current_request;//当前请求项指针
};

 

块设备表

extern struct blk_dev_struct blk_dev[NR_BLK_DEV];//每种块设备都在表中占有一项,共7项

 

请求项

struct request {
 int dev;  /* -1 if no request */
 int cmd;  /* READ or WRITE */
 int errors;//错误次数
 unsigned long sector;//起始扇区(********?*******)
 unsigned long nr_sectors;//读写扇区数
 char * buffer;//数据缓冲区
 struct task_struct * waiting;//发请求的进程

 struct buffer_head * bh;//缓冲区头
 struct request * next;
};

 

请求项数组

extern struct request request[NR_REQUEST];//共32项

 

请求项队列由请求项数组中的项构成,如图

 

三、硬盘驱动程序

涉及的文件:blk.h,hd.c,ll_rw_block.c

 

blk.h定义如上列出的数据结构,电梯算法(竟然是用一宏定义实现的),还有解锁指定的缓冲块,结束请求处理等extern inline类型

的函数(仅作为嵌入使用)。

 

hd.c是硬盘控制器驱动程序。在对硬盘控制器进行控制时,需要同时发送参数(6B)和命令(1B)。按照这个步骤,将hd.c文件中的几乎所有函数串起来。

  1.检测控制器空闲状态:读主状态寄存器,若BUSY_STAT(位7)为0,表示空闲,若在规定时间内控制器一直忙,则超时出错。该部分由

controller_ready函数实现:

static int controller_ready(void)
{
 int retries=10000;

 while (--retries && (inb_p(HD_STATUS)&0xc0)!=0x40);
 return (retries);
}

  2.检测驱动器就绪:判断主状态寄存器的READY_STAT(位6)是否为1;

static int drive_busy(void)
{
 unsigned int i;

 for (i = 0; i < 10000; i++)
  if (READY_STAT == (inb_p(HD_STATUS) & (BUSY_STAT|READY_STAT)))
   break;
 i = inb(HD_STATUS);
 i &= BUSY_STAT | READY_STAT | SEEK_STAT;
 if (i == (READY_STAT | SEEK_STAT))
  return(0);
 printk("HD controller times out/n/r");
 return(1);
}

  3.输出命令块,向对应端口输出参数和命令,hd_out函数实现。

static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
  unsigned int head,unsigned int cyl,unsigned int cmd,
  void (*intr_addr)(void))
{
 register int port asm("dx");

 if (drive>1 || head>15)
  panic("Trying to write bad sector");
 if (!controller_ready())
  panic("HD controller not ready");
 do_hd = intr_addr;
 outb_p(hd_info[drive].ctl,HD_CMD);
 port=HD_DATA;
 outb_p(hd_info[drive].wpcom>>2,++port);
 outb_p(nsect,++port);
 outb_p(sect,++port);
 outb_p(cyl,++port);
 outb_p(cyl>>8,++port);
 outb_p(0xA0|(drive<<4)|head,++port);
 outb(cmd,++port);
}
  4.等待中断产生。命令执行后,由硬盘控制器产生中断请求信号或置控制器状态为空闲,表明操作结束或请求扇区传输。hd.c中在中断

处理过程中调用的函数有5个,分别是,void unexpected_hd_interrupt(void),static void bad_rw_intr(void),static void read_intr(void),static void write_intr(void),static void recal_intr(void)。

  5.操作检测结果:cpu再次读主状态寄存器,若位0为0,则表示命令执行成功,否则失败,若失败,还可以进一步查询错误寄存器(HD_ERROR)取错误码。相关的函数是static int win_result(void)。

static int win_result(void)
{
 int i=inb_p(HD_STATUS);

 if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
  == (READY_STAT | SEEK_STAT))
  return(0); /* ok */
 if (i&1) i=inb(HD_ERROR);
 return (1);
}

 

整个流程由void do_hd_request(void)控制执行。

 

ll_rw_block.c程序主要用于执行底层块设备的读写操作,为快设备创建设备读写项,并插入到指定块设备请求队列中。其调用关系如图,

到文件系统时再讲。

抱歉!评论已关闭.