作者:opera Linux內核在缺省配置下最多支持10個IDE接口,IDE接口用ide_hwif_t結構來描述,每個IDE接口具有一對主-從驅動器接口,它們用ide_drive_t結構來描述,每個驅動器接口可接不同種類的IDE設備,如IDE硬盤,光驅等,它們用ide_driver_t結構來描述. 每個驅動器接口包含一個命令請求隊列,用request_queue_t結構來描述,具體的請求用request結構來描述. 多個IDE驅動器可以共享一個中斷,共享同一中斷的驅動器形成一個組,用ide_hwgroup_t結構來描述.ide_intr()是所有的ide驅動器所共用的硬件中斷入口,對之對應的ide_hwgroup_t指針將作為dev_id傳遞給ide_intr. 每次在讀寫某個驅動器之前,需要用ide_set_handler()來設置ide_intr將要調用的中斷函數指針.中斷產生以後,該函數指針被自動清除. do_rw_disk(drive,rq,block) 從邏輯扇區號block開始向IDE硬盤驅動器drive寫入rq所描述的內容. 以下是硬盤PIO傳輸模式的有關代碼. ; drivers/ide/ide-disk.c static ide_startstop_t do_rw_disk (ide_drive_t *drive, strUCt request *rq, unsigned long block) { if (IDE_CONTROL_REG) OUT_BYTE(drive->ctl,IDE_CONTROL_REG); OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG); if (drive->select.b.lba) { 如果是邏輯塊尋址模式 OUT_BYTE(block,IDE_SECTOR_REG); OUT_BYTE(block>>=8,IDE_LCYL_REG); OUT_BYTE(block>>=8,IDE_HCYL_REG); OUT_BYTE(((block>>&0x0f)drive->select.all,IDE_SELECT_REG); } else { unsigned int sect,head,cyl,track; track = block / drive->sect; sect = block % drive->sect + 1; OUT_BYTE(sect,IDE_SECTOR_REG); head = track % drive->head; cyl = track / drive->head; OUT_BYTE(cyl,IDE_LCYL_REG); OUT_BYTE(cyl>>8,IDE_HCYL_REG); OUT_BYTE(headdrive->select.all,IDE_SELECT_REG); } if (rq->cmd == READ) {{ ide_set_handler(drive, &read_intr, WAIT_CMD, NULL); WAIT_CMD為10秒超時 OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); return ide_started; } if (rq->cmd == WRITE) { ide_startstop_t startstop; OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG); if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); return startstop; } if (!drive->unmask) __cli(); /* local CPU only */ if (drive->mult_count) { 如果允許多扇區傳送 ide_hwgroup_t *hwgroup = HWGROUP(drive); /* * Ugh.. this part looks ugly because we MUST set up * the interrupt handler before outputting the first block * of data to be written. If we hit an error (corrupted buffer list) * in ide_multwrite(), then we need to remove the handler/timer * before returning. Fortunately, this NEVER happens (right?). * * Except when you get an error it seems... */ hwgroup->wrq = *rq; /* scratchpad */ ide_set_handler (drive, &multwrite_intr, WAIT_CMD, NULL); if (ide_multwrite(drive, drive->mult_count)) { unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); hwgroup->handler = NULL; del_timer(&hwgroup->timer); spin_unlock_irqrestore(&io_request_lock, flags); return ide_stopped; } } else { ide_set_handler (drive, &write_intr, WAIT_CMD, NULL); idedisk_output_data(drive, rq->buffer, SECTOR_WordS); 寫入一扇區SECTOR_WORDS=512/4 } return ide_started; } printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd); ide_end_request(0, HWGROUP(drive)); return ide_stopped; } void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_eXPiry_t *expiry) { unsigned long flags; ide_hwgroup_t *hwgroup = HWGROUP(drive); spin_lock_irqsave(&io_request_lock, flags); if (hwgroup->handler != NULL) { printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n", drive->name, hwgroup->handler, handler); } hwgroup->handler = handler; hwgroup->expiry = expiry; hwgroup->timer.expires = jiffies + timeout; add_timer(&hwgroup->timer); spin_unlock_irqrestore(&io_request_lock, flags); } static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) { if (drive->bswap) { idedisk_bswap_data(buffer, wcount); ide_output_data(drive, buffer, wcount); idedisk_bswap_data(buffer, wcount); } else ide_output_data(drive, buffer, wcount); } void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) { byte io_32bit = drive->io_32bit; if (io_32bit) { #if SUPPORT_VLB_SYNC if (io_32bit & 2) { unsigned long flags; __save_flags(flags); /* local CPU only */ __cli(); /* local CPU only */ do_vlb_sync(IDE_NSECTOR_REG); outsl(IDE_DATA_REG, buffer, wcount); __restore_flags(flags); /* local CPU only */ } else #endif /* SUPPORT_VLB_SYNC */ outsl(IDE_DATA_REG, buffer, wcount); } else { #if SUPPORT_SLOW_DATA_PORTS if (drive->slow) { unsigned short *ptr = (unsigned short *) buffer; while (wcount--) { outw_p(*ptr++, IDE_DATA_REG); outw_p(*ptr++, IDE_DATA_REG); } } else #endif /* SUPPORT_SLOW_DATA_PORTS */ outsw(IDE_DATA_REG, buffer, wcountcurrent_nr_sectors; if (nsect > mcount) nsect = mcount; mcount -= nsect; ; 這時mcount為剩余的必需傳送的扇區數 idedisk_output_data(drive, rq->buffer, nsectnr_sectors -= nsect)) current_nr_sectors -= nsect) == 0) { if ((rq->bh = rq->bh->b_reqnext) != NULL) {{ rq->current_nr_sectors = rq->bh->b_size>>9; rq->buffer = rq->bh->b_data; } else { spin_unlock_irqrestore(&io_request_lock, flags); printk("%s: buffer list corrupted (%ld, %ld, %d)\n", drive->name, rq->current_nr_sectors, rq->nr_sectors, nsect); ide_end_request(0, hwgroup); return 1; } } else { /* Fix the pointer.. we ate data */ rq->buffer += nsect hwif; if (!ide_ack_intr(hwif)) { spin_unlock_irqrestore(&i