📄 ide.c
字号:
if (nsect > mcount) nsect = mcount; mcount -= nsect; ide_output_data(drive, rq->buffer, nsect<<7);#ifdef DEBUG printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n", drive->name, rq->sector, (unsigned long) rq->buffer, nsect, rq->nr_sectors - nsect);#endif if ((rq->nr_sectors -= nsect) <= 0) break; if ((rq->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 { panic("%s: buffer list corrupted\n", drive->name); break; } } else { rq->buffer += nsect << 9; } } while (mcount);}/* * multwrite_intr() is the handler for disk multwrite interrupts */static void multwrite_intr (ide_drive_t *drive){ byte stat; int i; ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = &hwgroup->wrq; if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { if (stat & DRQ_STAT) { if (rq->nr_sectors) { ide_multwrite(drive, drive->mult_count); ide_set_handler (drive, &multwrite_intr, WAIT_CMD); return; } } else { if (!rq->nr_sectors) { /* all done? */ rq = hwgroup->rq; for (i = rq->nr_sectors; i > 0;){ i -= rq->current_nr_sectors; ide_end_request(1, hwgroup); } return; } } } ide_error(drive, "multwrite_intr", stat);}/* * Issue a simple drive command * The drive must be selected beforehand. */static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler){ ide_set_handler (drive, handler, WAIT_CMD); OUT_BYTE(drive->ctl,IDE_CONTROL_REG); OUT_BYTE(nsect,IDE_NSECTOR_REG); OUT_BYTE(cmd,IDE_COMMAND_REG);}/* * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. */static void set_multmode_intr (ide_drive_t *drive){ byte stat = GET_STAT(); sti(); if (OK_STAT(stat,READY_STAT,BAD_STAT)) { drive->mult_count = drive->mult_req; } else { drive->mult_req = drive->mult_count = 0; drive->special.b.recalibrate = 1; (void) ide_dump_status(drive, "set_multmode", stat); }}/* * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. */static void set_geometry_intr (ide_drive_t *drive){ byte stat = GET_STAT(); sti(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) ide_error(drive, "set_geometry_intr", stat);}/* * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. */static void recal_intr (ide_drive_t *drive){ byte stat = GET_STAT(); sti(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) ide_error(drive, "recal_intr", stat);}/* * mc_intr() is invoked on completion of a WIN_ACKMC cmd. */static void mc_intr (ide_drive_t *drive){ byte stat = GET_STAT(); sti(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) ide_error(drive, "mc_intr", stat); drive->special.b.mc = 0;}/* * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. */static void drive_cmd_intr (ide_drive_t *drive){ struct request *rq = HWGROUP(drive)->rq; byte *args = (byte *) rq->buffer; byte stat = GET_STAT(); sti(); if ((stat & DRQ_STAT) && args && args[3]) { byte io_32bit = drive->io_32bit; drive->io_32bit = 0; ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS); drive->io_32bit = io_32bit; stat = GET_STAT(); } if (OK_STAT(stat,READY_STAT,BAD_STAT)) ide_end_drive_cmd (drive, stat, GET_ERR()); else ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */}/* * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT * commands to a drive. It used to do much more, but has been scaled back. */static inline void do_special (ide_drive_t *drive){ special_t *s = &drive->special;#ifdef DEBUG printk("%s: do_special: 0x%02x\n", drive->name, s->all);#endif if (s->b.set_geometry) { s->b.set_geometry = 0; if (drive->media == ide_disk && !drive->no_geom) { OUT_BYTE(drive->sect,IDE_SECTOR_REG); OUT_BYTE(drive->cyl,IDE_LCYL_REG); OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG); OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG); if (!IS_PROMISE_DRIVE) ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr); } } else if (s->b.recalibrate) { s->b.recalibrate = 0; if (drive->media == ide_disk && !IS_PROMISE_DRIVE) ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr); } else if (s->b.set_tune) { ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc; s->b.set_tune = 0; if (tuneproc != NULL) tuneproc(drive, drive->tune_req); } else if (s->b.set_multmode) { s->b.set_multmode = 0; if (drive->media == ide_disk) { if (drive->id && drive->mult_req > drive->id->max_multsect) drive->mult_req = drive->id->max_multsect; if (!IS_PROMISE_DRIVE) ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr); } else drive->mult_req = 0; } else if (s->b.mc) { s->b.mc = 0; if (drive->media == ide_disk && !IS_PROMISE_DRIVE) ide_cmd(drive, WIN_ACKMC, drive->sect, &mc_intr); } else if (s->all) { int special = s->all; s->all = 0; printk("%s: bad special flag: 0x%02x\n", drive->name, special); }}/* * This routine busy-waits for the drive status to be not "busy". * It then checks the status for all of the "good" bits and none * of the "bad" bits, and if all is okay it returns 0. All other * cases return 1 after invoking ide_error() -- caller should just return. * * This routine should get fixed to not hog the cpu during extra long waits.. * That could be done by busy-waiting for the first jiffy or two, and then * setting a timer to wake up at half second intervals thereafter, * until timeout is achieved, before timing out. */int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout){ byte stat; unsigned long flags; udelay(1); /* spec allows drive 400ns to assert "BUSY" */ if ((stat = GET_STAT()) & BUSY_STAT) { save_flags(flags); sti(); timeout += jiffies; while ((stat = GET_STAT()) & BUSY_STAT) { if (jiffies > timeout) { restore_flags(flags); ide_error(drive, "status timeout", stat); return 1; } } restore_flags(flags); } udelay(1); /* allow status to settle, then read it again */ if (OK_STAT((stat = GET_STAT()), good, bad)) return 0; ide_error(drive, "status error", stat); return 1;}/* * do_rw_disk() issues READ and WRITE commands to a disk, * using LBA if supported, or CHS otherwise, to address sectors. * It also takes care of issuing special DRIVE_CMDs. */static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block){ ide_hwif_t *hwif = HWIF(drive); unsigned short io_base = hwif->io_base;#ifdef CONFIG_BLK_DEV_PROMISE int use_promise_io = 0;#endif /* CONFIG_BLK_DEV_PROMISE */ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); OUT_BYTE(rq->nr_sectors,io_base+IDE_NSECTOR_OFFSET);#ifdef CONFIG_BLK_DEV_PROMISE if (IS_PROMISE_DRIVE) { if (hwif->is_promise2 || rq->cmd == READ) { use_promise_io = 1; } } if (drive->select.b.lba || use_promise_io) {#else /* !CONFIG_BLK_DEV_PROMISE */ if (drive->select.b.lba) {#endif /* CONFIG_BLK_DEV_PROMISE */#ifdef DEBUG printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n", drive->name, (rq->cmd==READ)?"read":"writ", block, rq->nr_sectors, (unsigned long) rq->buffer);#endif OUT_BYTE(block,io_base+IDE_SECTOR_OFFSET); OUT_BYTE(block>>=8,io_base+IDE_LCYL_OFFSET); OUT_BYTE(block>>=8,io_base+IDE_HCYL_OFFSET); OUT_BYTE(((block>>8)&0x0f)|drive->select.all,io_base+IDE_SELECT_OFFSET); } else { unsigned int sect,head,cyl,track; track = block / drive->sect; sect = block % drive->sect + 1; OUT_BYTE(sect,io_base+IDE_SECTOR_OFFSET); head = track % drive->head; cyl = track / drive->head; OUT_BYTE(cyl,io_base+IDE_LCYL_OFFSET); OUT_BYTE(cyl>>8,io_base+IDE_HCYL_OFFSET); OUT_BYTE(head|drive->select.all,io_base+IDE_SELECT_OFFSET);#ifdef DEBUG printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n", drive->name, (rq->cmd==READ)?"read":"writ", cyl, head, sect, rq->nr_sectors, (unsigned long) rq->buffer);#endif }#ifdef CONFIG_BLK_DEV_PROMISE if (use_promise_io) { do_promise_io (drive, rq); return; }#endif /* CONFIG_BLK_DEV_PROMISE */ if (rq->cmd == READ) {#ifdef CONFIG_BLK_DEV_TRITON if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive))) return;#endif /* CONFIG_BLK_DEV_TRITON */ ide_set_handler(drive, &read_intr, WAIT_CMD); OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+IDE_COMMAND_OFFSET); return; } if (rq->cmd == WRITE) {#ifdef CONFIG_BLK_DEV_TRITON if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive))) return;#endif /* CONFIG_BLK_DEV_TRITON */ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, io_base+IDE_COMMAND_OFFSET); if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk("%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); return; } if (!drive->unmask) cli(); if (drive->mult_count) { HWGROUP(drive)->wrq = *rq; /* scratchpad */ ide_set_handler (drive, &multwrite_intr, WAIT_CMD); ide_multwrite(drive, drive->mult_count); } else { ide_set_handler (drive, &write_intr, WAIT_CMD); ide_output_data(drive, rq->buffer, SECTOR_WORDS); } return; } printk("%s: bad command: %d\n", drive->name, rq->cmd); ide_end_request(0, HWGROUP(drive));}/* * execute_drive_cmd() issues a special drive command, * usually initiated by ioctl() from the external hdparm program. */static void execute_drive_cmd (ide_drive_t *drive, struct request *rq){ byte *args = rq->buffer; if (args) {#ifdef DEBUG printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n", drive->name, args[0], args[1], args[2], args[3]);#endif OUT_BYTE(args[2],IDE_FEATURE_REG); ide_cmd(drive, args[0], args[1], &drive_cmd_intr); return; } else { /* * NULL is actually a valid way of waiting for * all current requests to be flushed from the queue. */#ifdef DEBUG printk("%s: DRIVE_CMD (null)\n", drive->name);#endif ide_end_drive_cmd(drive, GET_STAT(), GET_ERR()); return; }}/* * do_request() initiates handling of a new I/O request */static inline void do_request (ide_hwif_t *hwif, struct request *rq){ unsigned int minor, unit; unsigned long block, blockend; ide_drive_t *drive; sti();#ifdef DEBUG printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);#endif minor = MINOR(rq->rq_dev); unit = minor >> PARTN_BITS; if (MAJOR(rq->rq_dev) != hwif->major || unit >= MAX_DRIVES) { printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev)); goto kill_rq; } drive = &hwif->drives[unit];#ifdef DEBUG if (rq->bh && !buffer_locked(rq->bh)) { printk("%s: block not locked\n", drive->name); goto kill_rq; }#endif block = rq->sector; blockend = block + rq->nr_sectors; if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {#ifdef MACH printk ("%s%c: bad access: block=%ld, count=%ld, blockend=%ld, nr_sects%ld\n", drive->name, (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors, blockend, drive->part[minor&PARTN_MASK].nr_sects);#else printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name, (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);#endif goto kill_rq; } block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;#if FAKE_FDISK_FOR_EZDRIVE if (block == 0 && drive->remap_0_to_1) block = 1; /* redirect MBR access to EZ-Drive partn table */#endif /* FAKE_FDISK_FOR_EZDRIVE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -