?? hd.c
字號:
{ 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); // 若ERR_STAT 置位,則讀取錯誤寄存器。 return (1);}//// 向硬盤控制器發送命令塊(參見列表后的說明)。// 調用參數:drive - 硬盤號(0-1); nsect - 讀寫扇區數;// sect - 起始扇區; head - 磁頭號;// cyl - 柱面號; cmd - 命令碼;// *intr_addr() - 硬盤中斷處理程序中將調用的C 處理函數。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"); // port 變量對應寄存器dx。 if (drive > 1 || head > 15) // 如果驅動器號(0,1)>1 或磁頭號>15,則程序不支持。 panic ("Trying to write bad sector"); if (!controller_ready ()) // 如果等待一段時間后仍未就緒則出錯,死機。 panic ("HD controller not ready"); do_hd = intr_addr; // do_hd 函數指針將在硬盤中斷程序中被調用。 outb_p (hd_info[drive].ctl, HD_CMD); // 向控制寄存器(0x3f6)輸出控制字節。 port = HD_DATA; // 置dx 為數據寄存器端口(0x1f0)。 outb_p (hd_info[drive].wpcom >> 2, ++port); // 參數:寫預補償柱面號(需除4)。 outb_p (nsect, ++port); // 參數:讀/寫扇區總數。 outb_p (sect, ++port); // 參數:起始扇區。 outb_p (cyl, ++port); // 參數:柱面號低8 位。 outb_p (cyl >> 8, ++port); // 參數:柱面號高8 位。 outb_p (0xA0 | (drive << 4) | head, ++port); // 參數:驅動器號+磁頭號。 outb (cmd, ++port); // 命令:硬盤控制命令。}//// 等待硬盤就緒。也即循環等待主狀態控制器忙標志位復位。若僅有就緒或尋道結束標志// 置位,則成功,返回0。若經過一段時間仍為忙,則返回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) // 若僅有就緒或尋道結束標志,則返回0。 return (0); printk ("HD controller times out\n\r"); // 否則等待超時,顯示信息。并返回1。 return (1);}//// 診斷復位(重新校正)硬盤控制器。static void reset_controller (void){ int i; outb (4, HD_CMD); // 向控制寄存器端口發送控制字節(4-復位)。 for (i = 0; i < 100; i++) nop (); // 等待一段時間(循環空操作)。 outb (hd_info[0].ctl & 0x0f, HD_CMD); // 再發送正常的控制字節(不禁止重試、重讀)。 if (drive_busy ()) // 若等待硬盤就緒超時,則顯示出錯信息。 printk ("HD-controller still busy\n\r"); if ((i = inb (HD_ERROR)) != 1) // 取錯誤寄存器,若不等于1(無錯誤)則出錯。 printk ("HD-controller reset failed: %02x\n\r", i);}//// 復位硬盤nr。首先復位(重新校正)硬盤控制器。然后發送硬盤控制器命令“建立驅動器參數”,// 其中recal_intr()是在硬盤中斷處理程序中調用的重新校正處理函數。static void reset_hd (int nr){ reset_controller (); hd_out (nr, hd_info[nr].sect, hd_info[nr].sect, hd_info[nr].head - 1, hd_info[nr].cyl, WIN_SPECIFY, &recal_intr);}//// 意外硬盤中斷調用函數。// 發生意外硬盤中斷時,硬盤中斷處理程序中調用的默認C 處理函數。在被調用函數指針為空時// 調用該函數。參見(kernel/system_call.s,241 行)。void unexpected_hd_interrupt (void){ printk ("Unexpected HD interrupt\n\r");}//// 讀寫硬盤失敗處理調用函數。static void bad_rw_intr (void){ if (++CURRENT->errors >= MAX_ERRORS) // 如果讀扇區時的出錯次數大于或等于7 次時, end_request (0); // 則結束請求并喚醒等待該請求的進程,而且// 對應緩沖區更新標志復位(沒有更新)。 if (CURRENT->errors > MAX_ERRORS / 2) // 如果讀一扇區時的出錯次數已經大于3 次, reset = 1; // 則要求執行復位硬盤控制器操作。}//// 讀操作中斷調用函數。將在執行硬盤中斷處理程序中被調用。static void read_intr (void){ if (win_result ()) { // 若控制器忙、讀寫錯或命令執行錯, bad_rw_intr (); // 則進行讀寫硬盤失敗處理 do_hd_request (); // 然后再次請求硬盤作相應(復位)處理。 return; } port_read (HD_DATA, CURRENT->buffer, 256); // 將數據從數據寄存器口讀到請求結構緩沖區。 CURRENT->errors = 0; // 清出錯次數。 CURRENT->buffer += 512; // 調整緩沖區指針,指向新的空區。 CURRENT->sector++; // 起始扇區號加1, if (--CURRENT->nr_sectors) { // 如果所需讀出的扇區數還沒有讀完,則 do_hd = &read_intr; // 再次置硬盤調用C 函數指針為read_intr() return; // 因為硬盤中斷處理程序每次調用do_hd 時 } // 都會將該函數指針置空。參見system_call.s end_request (1); // 若全部扇區數據已經讀完,則處理請求結束事宜, do_hd_request (); // 執行其它硬盤請求操作。}//// 寫扇區中斷調用函數。在硬盤中斷處理程序中被調用。// 在寫命令執行后,會產生硬盤中斷信號,執行硬盤中斷處理程序,此時在硬盤中斷處理程序中調用的// C 函數指針do_hd()已經指向write_intr(),因此會在寫操作完成(或出錯)后,執行該函數。static void write_intr (void){ if (win_result ()) { // 如果硬盤控制器返回錯誤信息, bad_rw_intr (); // 則首先進行硬盤讀寫失敗處理, do_hd_request (); // 然后再次請求硬盤作相應(復位)處理, return; // 然后返回(也退出了此次硬盤中斷)。 } if (--CURRENT->nr_sectors) { // 否則將欲寫扇區數減1,若還有扇區要寫,則 CURRENT->sector++; // 當前請求起始扇區號+1, CURRENT->buffer += 512; // 調整請求緩沖區指針, do_hd = &write_intr; // 置硬盤中斷程序調用函數指針為write_intr(), port_write (HD_DATA, CURRENT->buffer, 256); // 再向數據寄存器端口寫256 字節。 return; // 返回等待硬盤再次完成寫操作后的中斷處理。 } end_request (1); // 若全部扇區數據已經寫完,則處理請求結束事宜, do_hd_request (); // 執行其它硬盤請求操作。}//// 硬盤重新校正(復位)中斷調用函數。在硬盤中斷處理程序中被調用。// 如果硬盤控制器返回錯誤信息,則首先進行硬盤讀寫失敗處理,然后請求硬盤作相應(復位)處理。static void recal_intr (void){ if (win_result ()) bad_rw_intr (); do_hd_request ();}// 執行硬盤讀寫請求操作。void do_hd_request (void){ int i, r; unsigned int block, dev; unsigned int sec, head, cyl; unsigned int nsect; INIT_REQUEST; // 檢測請求項的合法性(參見kernel/blk_drv/blk.h,127)。// 取設備號中的子設備號(見列表后對硬盤設備號的說明)。子設備號即是硬盤上的分區號。 dev = MINOR (CURRENT->dev); // CURRENT 定義為(blk_dev[MAJOR_NR].current_request)。 block = CURRENT->sector; // 請求的起始扇區。// 如果子設備號不存在或者起始扇區大于該分區扇區數-2,則結束該請求,并跳轉到標號repeat 處// (定義在INIT_REQUEST 開始處)。因為一次要求讀寫2 個扇區(512*2 字節),所以請求的扇區號// 不能大于分區中最后倒數第二個扇區號。 if (dev >= 5 * NR_HD || block + 2 > hd[dev].nr_sects) { end_request (0); goto repeat; // 該標號在blk.h 最后面。 } block += hd[dev].start_sect; // 將所需讀的塊對應到整個硬盤上的絕對扇區號。 dev /= 5; // 此時dev 代表硬盤號(0 或1)。// 下面嵌入匯編代碼用來從硬盤信息結構中根據起始扇區號和每磁道扇區數計算在磁道中的// 扇區號(sec)、所在柱面號(cyl)和磁頭號(head)。__asm__ ("divl %4": "=a" (block), "=d" (sec):"" (block), "1" (0), "r" (hd_info[dev]. sect));__asm__ ("divl %4": "=a" (cyl), "=d" (head):"" (block), "1" (0), "r" (hd_info[dev]. head)); sec++; nsect = CURRENT->nr_sectors; // 欲讀/寫的扇區數。// 如果reset 置1,則執行復位操作。復位硬盤和控制器,并置需要重新校正標志,返回。 if (reset) { reset = 0; recalibrate = 1; reset_hd (CURRENT_DEV); return; }// 如果重新校正標志(recalibrate)置位,則首先復位該標志,然后向硬盤控制器發送重新校正命令。 if (recalibrate) { recalibrate = 0; hd_out (dev, hd_info[CURRENT_DEV].sect, 0, 0, 0, WIN_RESTORE, &recal_intr); return; }// 如果當前請求是寫扇區操作,則發送寫命令,循環讀取狀態寄存器信息并判斷請求服務標志// DRQ_STAT 是否置位。DRQ_STAT 是硬盤狀態寄存器的請求服務位(include/linux/hdreg.h,27)。 if (CURRENT->cmd == WRITE) { hd_out (dev, nsect, sec, head, cyl, WIN_WRITE, &write_intr); for (i = 0; i < 3000 && !(r = inb_p (HD_STATUS) & DRQ_STAT); i++)/* nothing */ ;// 如果請求服務位置位則退出循環。若等到循環結束也沒有置位,則此次寫硬盤操作失敗,去處理// 下一個硬盤請求。否則向硬盤控制器數據寄存器端口HD_DATA 寫入1 個扇區的數據。 if (!r) { bad_rw_intr (); goto repeat; // 該標號在blk.h 最后面,也即跳到301 行。 } port_write (HD_DATA, CURRENT->buffer, 256);// 如果當前請求是讀硬盤扇區,則向硬盤控制器發送讀扇區命令。 } else if (CURRENT->cmd == READ) { hd_out (dev, nsect, sec, head, cyl, WIN_READ, &read_intr); } else panic ("unknown hd-command");}// 硬盤系統初始化。void hd_init (void){ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; // do_hd_request()。 set_intr_gate (0x2E, &hd_interrupt); // 設置硬盤中斷門向量 int 0x2E(46)。// hd_interrupt 在(kernel/system_call.s,221)。 outb_p (inb_p (0x21) & 0xfb, 0x21); // 復位接聯的主8259A int2 的屏蔽位,允許從片// 發出中斷請求信號。 outb (inb_p (0xA1) & 0xbf, 0xA1); // 復位硬盤的中斷請求屏蔽位(在從片上),允許// 硬盤控制器發送中斷請求信號。}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -