Linux-0.0.1内核阅读连载笔记-2013.08.23

__________________________________________________________

../kernel/Vsprintf.c

__________________________________________________________

#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>

/*
 * This code handles all hd-interrupts, and read/write requests to
 * the hard-disk. It is relatively straigthforward (not obvious maybe,
 * but interrupts never are), while still being efficient, and never
 * disabling interrupts (except to overcome possible race-condition).
 * The elevator block-seek algorithm doesn't need to disable interrupts
 * due to clever programming.
 */

/* Max read/write errors/sector */
#define MAX_ERRORS 5
#define MAX_HD  2
#define NR_REQUEST 32

/*
 *  This struct defines the HD's and their types.
 *  Currently defined for CP3044's, ie a modified
 *  type 17.
 */
static struct hd_i_struct{
 int head,sect,cyl,wpcom,lzone,ctl;
 } hd_info[]= { HD_TYPE };

#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))

static struct hd_struct {
 long start_sect;
 long nr_sects;
} hd[5*MAX_HD]={{0,0},};   //一个硬盘可分5个区,共有2个硬盘

static struct hd_request {
 int hd;  /* -1 if no request */
 int nsector;
 int sector;
 int head;
 int cyl;
 int cmd;
 int errors;
 struct buffer_head * bh;
 struct hd_request * next;
} request[NR_REQUEST];

#define IN_ORDER(s1,s2) \
((s1)->hd<(s2)->hd || (s1)->hd==(s2)->hd && \
((s1)->cyl<(s2)->cyl || (s1)->cyl==(s2)->cyl && \
((s1)->head<(s2)->head || (s1)->head==(s2)->head && \
((s1)->sector<(s2)->sector))))

static struct hd_request * this_request = NULL;

static int sorting=0;

static void do_request(void);
static void reset_controller(void);
static void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
 unsigned int cyl,struct buffer_head * bh);
void hd_init(void);

#define port_read(port,buf,nr) \
//下面的内嵌汇编的意思是:
// movl port %edx
// movl buf %edi
// movl nr %ecx
// cld
// rep insw  //从edx指定的port传送字到es:edi指定的buf中
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")

#define port_write(port,buf,nr) \
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si") //其中outsw是从ds:si指定的buf中传送字到edx指定的port

extern void hd_interrupt(void);   //在system_call.S中定义

static struct task_struct * wait_for_request=NULL;

static inline void lock_buffer(struct buffer_head * bh)
{
 if (bh->b_lock)    //此缓冲区是否上锁
  printk("hd.c: buffer multiply locked\n");
 bh->b_lock=1;
}

static inline void unlock_buffer(struct buffer_head * bh)
{
 if (!bh->b_lock)
  printk("hd.c: free buffer being unlocked\n");
 bh->b_lock=0;
 wake_up(&bh->b_wait);
}

static inline void wait_on_buffer(struct buffer_head * bh)
{
 cli();
 while (bh->b_lock)
  sleep_on(&bh->b_wait);  //进入等待对列
 sti();
}

void rw_hd(int rw, struct buffer_head * bh) //缓冲区和磁盘之间交换数据
{
 unsigned int block,dev;
 unsigned int sec,head,cyl;

 block = bh->b_blocknr << 1;   //默认1块=1024字节,二个扇区
 dev = MINOR(bh->b_dev);
 if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) //是否超过了分区,此分区是否满了
  return;
 block += hd[dev].start_sect;  //此分区的开始扇区+block,即在硬盘中定开始的位置
 dev /= 5;     //看看是哪个硬盘
 __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), 
  "r" (hd_info[dev].sect));     //确定扇区号
 __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
  "r" (hd_info[dev].head));     //确定光头号
 rw_abs_hd(rw,dev,sec+1,head,cyl,bh); //实际写盘函数
}

/* This may be used only once, enforced by 'static int callable' */
int sys_setup(void)
{
 static int callable = 1;
 int i,drive;
 struct partition *p;

 if (!callable)
  return -1;
 callable = 0;
 for (drive=0 ; drive<NR_HD ; drive++) {
  rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer); //读取引导扇区512字节到start_buffer中
  if (!start_buffer->b_uptodate) {
   printk("Unable to read partition table of drive %d\n\r",
    drive);
   panic("");
  }
  if (start_buffer->b_data[510] != 0x55 || (unsigned char)
      start_buffer->b_data[511] != 0xAA) {    //分析引导扇区是否合法,合法应为55AA
   printk("Bad partition table on drive %d\n\r",drive);
   panic("");
  }
  p = 0x1BE + (void *)start_buffer->b_data;  //指针指向分区信息的开始位置
  for (i=1;i<5;i++,p++) {    //保存分区信息
   hd[i+5*drive].start_sect = p->start_sect;
   hd[i+5*drive].nr_sects = p->nr_sects;
  }
 }
 printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
 mount_root();    //在super.c中定义
 return (0);
}

/*
 * This is the pointer to a routine to be executed at every hd-interrupt.
 * Interesting way of doing things, but should be rather practical.
 */
void (*do_hd)(void) = NULL;

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

 while (--retries && (inb(HD_STATUS)&0xc0)!=0x40); //0x40是第六个位置1为驱动器准备好,0x1f7端口是读取硬盘状态
 return (retries);
}

static int win_result(void)
{
 int i=inb(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); //0x1f1是硬盘错误寄存器,检查上一个命令是否出错
 return (1);
}

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) //只支持1个硬盘和14个头
  panic("Trying to write bad sector");
 if (!controller_ready())
  panic("HD controller not ready");
 do_hd = intr_addr;  //硬盘写或读操作指针
 outb(_CTL,HD_CMD);  //0x3f6是硬盘控制寄存器,置0为控制器为普通操作
 port=HD_DATA;   //0x1f0硬盘数据寄存器
 outb_p(_WPCOM,++port); //0x1f1,写预补偿,在从磁柱300X4处开始写
 outb_p(nsect,++port); //0x1f2是硬盘扇区记数寄存器,将要传送的扇区数放入该寄存器
 outb_p(sect,++port);  //0x1f3是硬盘扇区号寄存器,记录当前的扇区号
 outb_p(cyl,++port);  //0x1f4硬盘磁柱低寄存器,保存起始磁柱号的低8字节
 outb_p(cyl>>8,++port); //0x1f5硬盘磁柱高寄存器
 outb_p(0xA0|(drive<<4)|head,++port); //0x1f6硬盘驱动器和磁头寄存器,这里选择主盘(高4位为1010),低4位为磁头号
 outb(cmd,++port);  //0x1f7硬盘命令输出寄存器,主要向硬盘发出命令
}

static int drive_busy(void)
{
 unsigned int i;

 for (i = 0; i < 100000; i++) //循环,直到驱动器为READY_STAT或超时
  if (READY_STAT == (inb(HD_STATUS) & (BUSY_STAT | READY_STAT)))
   break;
 i = inb(HD_STATUS);   //再读硬盘状态
 i &= BUSY_STAT | READY_STAT | SEEK_STAT;
 if (i == READY_STAT | SEEK_STAT) //如果处于读状态或寻找状态,OK
  return(0);
 printk("HD controller times out\n\r");
 return(1);
}

static void reset_controller(void)
{
 int i;

 outb(4,HD_CMD);  //0x3f6是硬盘适配器控制寄存器,第二位置1为重启控制器
 for(i = 0; i < 1000; i++) nop(); //等待一会儿
 outb(0,HD_CMD);  //使驱动器处于普通状态
 for(i = 0; i < 10000 && drive_busy(); i++) /* nothing */;
 if (drive_busy())
  printk("HD-controller still busy\n\r");
 if((i = inb(ERR_STAT)) != 1)
  printk("HD-controller reset failed: %02x\n\r",i);
}

static void reset_hd(int nr)
{
 reset_controller();
 hd_out(nr,_SECT,_SECT,_HEAD-1,_CYL,WIN_SPECIFY,&do_request); //do_request是重新处理请求队列
}

void unexpected_hd_interrupt(void)
{
 panic("Unexpected HD interrupt\n\r");
}

static void bad_rw_intr(void)
{
 int i = this_request->hd;

 if (this_request->errors++ >= MAX_ERRORS) { //如果错误超过有效的最大错误数,放弃这次请求
  this_request->bh->b_uptodate = 0;
  unlock_buffer(this_request->bh);
  wake_up(&wait_for_request);
  this_request->hd = -1;
  this_request=this_request->next;
 }
 reset_hd(i);
}

static void read_intr(void)
{
 if (win_result()) {
  bad_rw_intr();
  return;
 }
 port_read(HD_DATA,this_request->bh->b_data+
  512*(this_request->nsector&1),256); //是从b_data指定位置读512字节
 this_request->errors = 0;
 if (--this_request->nsector)  //如果还有要传的数据,直接返回,不从请求队列中删除该请求
  return;
//以下6行代码是指数据传送完毕,就从请求队列中删除该请求
 this_request->bh->b_uptodate = 1;
 this_request->bh->b_dirt = 0;
 wake_up(&wait_for_request);
 unlock_buffer(this_request->bh);
 this_request->hd = -1;
 this_request=this_request->next;
 do_request();    //处理其他请求
}

static void write_intr(void)
{
 if (win_result()) {
  bad_rw_intr();  //如果有错误,重启驱动器,再继续处理请求
  return;
 }
 if (--this_request->nsector) {
  port_write(HD_DATA,this_request->bh->b_data+512,256); //向硬盘数据寄存器写数据
  return;
 }
 this_request->bh->b_uptodate = 1;
 this_request->bh->b_dirt = 0;
 wake_up(&wait_for_request);
 unlock_buffer(this_request->bh);
 this_request->hd = -1;
 this_request=this_request->next;
 do_request();    //处理下一个请求
}

static void do_request(void)
{
 int i,r;

 if (sorting)
  return;
 if (!this_request) {
  do_hd=NULL;
  return;
 }
 if (this_request->cmd == WIN_WRITE) {
  hd_out(this_request->hd,this_request->nsector,this_request->
   sector,this_request->head,this_request->cyl,
   this_request->cmd,&write_intr);
  for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) //循环,直到控制器指示准备好
   /* nothing */ ;
  if (!r) {     //如果超时驱动器仍未准备好,重设该驱动器
   reset_hd(this_request->hd);
   return;
  }
  port_write(HD_DATA,this_request->bh->b_data+
   512*(this_request->nsector&1),256);  //传送数据
 } else if (this_request->cmd == WIN_READ) {
  hd_out(this_request->hd,this_request->nsector,this_request->
   sector,this_request->head,this_request->cyl,
   this_request->cmd,&read_intr);
 } else
  panic("unknown hd-command");
}

/*
 * add-request adds a request to the linked list.
 * It sets the 'sorting'-variable when doing something
 * that interrupts shouldn't touch.
 */
static void add_request(struct hd_request * req)
{
 struct hd_request * tmp;

 if (req->nsector != 2)
  panic("nsector!=2 not implemented");
/*
 * Not to mess up the linked lists, we never touch the two first
 * entries (not this_request, as it is used by current interrups,
 * and not this_request->next, as it can be assigned to this_request).
 * This is not too high a price to pay for the ability of not
 * disabling interrupts.
 */
 sorting=1;
 if (!(tmp=this_request))
  this_request=req;
 else {
  if (!(tmp->next))
   tmp->next=req;
  else {
   tmp=tmp->next;
   for ( ; tmp->next ; tmp=tmp->next)
    if ((IN_ORDER(tmp,req) ||
        !IN_ORDER(tmp,tmp->next)) &&
        IN_ORDER(req,tmp->next))
     break;
   req->next=tmp->next;
   tmp->next=req;
  }
 }
 sorting=0;
/*
 * NOTE! As a result of sorting, the interrupts may have died down,
 * as they aren't redone due to locking with sorting=1. They might
 * also never have started, if this is the first request in the queue,
 * so we restart them if necessary.
 */
 if (!do_hd)
  do_request();
}

void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
 unsigned int cyl,struct buffer_head * bh)     //根据硬盘参数做磁盘读写
{
 struct hd_request * req;

 if (rw!=READ && rw!=WRITE)
  panic("Bad hd command, must be R/W");
 lock_buffer(bh);
repeat:
 for (req=0+request ; req<NR_REQUEST+request ; req++)
  if (req->hd<0)     //hd=-1表示没有请求
   break;
 if (req==NR_REQUEST+request) {    //不能超过最大的请求数
  sleep_on(&wait_for_request);
  goto repeat;
 }
 req->hd=nr;
 req->nsector=2;      //==1024字节
 req->sector=sec;
 req->head=head;
 req->cyl=cyl;
 req->cmd = ((rw==READ)?WIN_READ:WIN_WRITE);
 req->bh=bh;
 req->errors=0;
 req->next=NULL;
 add_request(req);
 wait_on_buffer(bh);      //进入等待
}

void hd_init(void)
{
 int i;

 for (i=0 ; i<NR_REQUEST ; i++) {    //初始化请求队列
  request[i].hd = -1;
  request[i].next = NULL;
 }
 for (i=0 ; i<NR_HD ; i++) {     //初始化硬盘分区
  hd[i*5].start_sect = 0;
  hd[i*5].nr_sects = hd_info[i].head*
    hd_info[i].sect*hd_info[i].cyl;
 }
 set_trap_gate(0x2E,&hd_interrupt);    //登记硬盘中断处理程序,中断号为0x2E
 outb_p(inb_p(0x21)&0xfb,0x21);    //开放IRQ2(中断控制器1)
 outb(inb_p(0xA1)&0xbf,0xA1);    //开放IRQ14,硬盘控制器
}

 

上一篇:Linux-0.0.1内核阅读连载笔记-2013.08.22


下一篇:随感而发