⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ide.c

📁 《嵌入式系统设计与实例开发实验教材II:基于ARM9微处理器与Linux操作系统》IDE—CF卡模块读写实验
💻 C
📖 第 1 页 / 共 5 页
字号:
	/*	 * Some weird controller like resetting themselves to a strange	 * state when the disks are reset this way. At least, the Winbond	 * 553 documentation says that	 */	if (hwif->resetproc != NULL)		hwif->resetproc(drive);#endif	/* OK_TO_RESET_CONTROLLER */	__restore_flags (flags);	/* local CPU only */	return ide_started;}/* * ide_do_reset() is the entry point to the drive/interface reset code. */ide_startstop_t ide_do_reset (ide_drive_t *drive){	return do_reset1 (drive, 0);}/* * Clean up after success/failure of an explicit drive cmd */void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err){	unsigned long flags;	struct request *rq;	spin_lock_irqsave(&io_request_lock, flags);	rq = HWGROUP(drive)->rq;	spin_unlock_irqrestore(&io_request_lock, flags);	if (rq->cmd == IDE_DRIVE_CMD) {		byte *args = (byte *) rq->buffer;		rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);		if (args) {			args[0] = stat;			args[1] = err;			args[2] = IN_BYTE(IDE_NSECTOR_REG);		}	} else if (rq->cmd == IDE_DRIVE_TASK) {		byte *args = (byte *) rq->buffer;		rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);		if (args) {			args[0] = stat;			args[1] = err;			args[2] = IN_BYTE(IDE_NSECTOR_REG);			args[3] = IN_BYTE(IDE_SECTOR_REG);			args[4] = IN_BYTE(IDE_LCYL_REG);			args[5] = IN_BYTE(IDE_HCYL_REG);			args[6] = IN_BYTE(IDE_SELECT_REG);		}	}	spin_lock_irqsave(&io_request_lock, flags);	blkdev_dequeue_request(rq);	HWGROUP(drive)->rq = NULL;	end_that_request_last(rq);	spin_unlock_irqrestore(&io_request_lock, flags);}/* * Error reporting, in human readable form (luxurious, but a memory hog). */byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat){	unsigned long flags;	byte err = 0;	__save_flags (flags);	/* local CPU only */	ide__sti();		/* local CPU only */	printk("%s: %s: status=0x%02x", drive->name, msg, stat);#if FANCY_STATUS_DUMPS	printk(" { ");	if (stat & BUSY_STAT)		printk("Busy ");	else {		if (stat & READY_STAT)	printk("DriveReady ");		if (stat & WRERR_STAT)	printk("DeviceFault ");		if (stat & SEEK_STAT)	printk("SeekComplete ");		if (stat & DRQ_STAT)	printk("DataRequest ");		if (stat & ECC_STAT)	printk("CorrectedError ");		if (stat & INDEX_STAT)	printk("Index ");		if (stat & ERR_STAT)	printk("Error ");	}	printk("}");#endif	/* FANCY_STATUS_DUMPS */	printk("\n");	if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {		err = GET_ERR();		printk("%s: %s: error=0x%02x", drive->name, msg, err);#if FANCY_STATUS_DUMPS		if (drive->media == ide_disk) {			printk(" { ");			if (err & ABRT_ERR)	printk("DriveStatusError ");			if (err & ICRC_ERR)	printk("%s", (err & ABRT_ERR) ? "BadCRC " : "BadSector ");			if (err & ECC_ERR)	printk("UncorrectableError ");			if (err & ID_ERR)	printk("SectorIdNotFound ");			if (err & TRK0_ERR)	printk("TrackZeroNotFound ");			if (err & MARK_ERR)	printk("AddrMarkNotFound ");			printk("}");			if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) {				byte cur = IN_BYTE(IDE_SELECT_REG);				if (cur & 0x40) {	/* using LBA? */					printk(", LBAsect=%ld", (unsigned long)					 ((cur&0xf)<<24)					 |(IN_BYTE(IDE_HCYL_REG)<<16)					 |(IN_BYTE(IDE_LCYL_REG)<<8)					 | IN_BYTE(IDE_SECTOR_REG));				} else {					printk(", CHS=%d/%d/%d",					 (IN_BYTE(IDE_HCYL_REG)<<8) +					  IN_BYTE(IDE_LCYL_REG),					  cur & 0xf,					  IN_BYTE(IDE_SECTOR_REG));				}				if (HWGROUP(drive) && HWGROUP(drive)->rq)					printk(", sector=%ld", HWGROUP(drive)->rq->sector);			}		}#endif	/* FANCY_STATUS_DUMPS */		printk("\n");	}	__restore_flags (flags);	/* local CPU only */	return err;}/* * try_to_flush_leftover_data() is invoked in response to a drive * unexpectedly having its DRQ_STAT bit set.  As an alternative to * resetting the drive, this routine tries to clear the condition * by read a sector's worth of data from the drive.  Of course, * this may not help if the drive is *waiting* for data from *us*. */static void try_to_flush_leftover_data (ide_drive_t *drive){	int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;	if (drive->media != ide_disk)		return;	while (i > 0) {		u32 buffer[16];		unsigned int wcount = (i > 16) ? 16 : i;		i -= wcount;		ide_input_data (drive, buffer, wcount);	}}/* * ide_error() takes action based on the error returned by the drive. */ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat){	struct request *rq;	byte err;	err = ide_dump_status(drive, msg, stat);	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)		return ide_stopped;	/* retry only "normal" I/O: */	if (rq->cmd == IDE_DRIVE_CMD || rq->cmd == IDE_DRIVE_TASK) {		rq->errors = 1;		ide_end_drive_cmd(drive, stat, err);		return ide_stopped;	}	if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */		rq->errors |= ERROR_RESET;	} else {		if (drive->media == ide_disk && (stat & ERR_STAT)) {			/* err has different meaning on cdrom and tape */			if (err == ABRT_ERR) {				if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)					return ide_stopped; /* some newer drives don't support WIN_SPECIFY */			} else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) {				drive->crc_count++; /* UDMA crc error -- just retry the operation */			} else if (err & (BBD_ERR | ECC_ERR))	/* retries won't help these */				rq->errors = ERROR_MAX;			else if (err & TRK0_ERR)	/* help it find track zero */				rq->errors |= ERROR_RECAL;		}		if ((stat & DRQ_STAT) && rq->cmd != WRITE)			try_to_flush_leftover_data(drive);	}	if (GET_STAT() & (BUSY_STAT|DRQ_STAT))		OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG);	/* force an abort */	if (rq->errors >= ERROR_MAX) {		if (drive->driver != NULL)			DRIVER(drive)->end_request(0, HWGROUP(drive));		else	 		ide_end_request(0, HWGROUP(drive));	} else {		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {			++rq->errors;			return ide_do_reset(drive);		}		if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)			drive->special.b.recalibrate = 1;		++rq->errors;	}	return ide_stopped;}/* * Issue a simple drive command * The drive must be selected beforehand. */void ide_cmd (ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler){	ide_set_handler (drive, handler, WAIT_CMD, NULL);	if (IDE_CONTROL_REG)		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* clear nIEN */	SELECT_MASK(HWIF(drive),drive,0);	OUT_BYTE(nsect,IDE_NSECTOR_REG);	OUT_BYTE(cmd,IDE_COMMAND_REG);}/* * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. */static ide_startstop_t drive_cmd_intr (ide_drive_t *drive){	struct request *rq = HWGROUP(drive)->rq;	byte *args = (byte *) rq->buffer;	byte stat = GET_STAT();	int retries = 10;	ide__sti();	/* local CPU only */	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;		while (((stat = GET_STAT()) & BUSY_STAT) && retries--)			udelay(100);	}	if (!OK_STAT(stat, READY_STAT, BAD_STAT))		return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */	ide_end_drive_cmd (drive, stat, GET_ERR());	return ide_stopped;}/* * 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 ide_startstop_t 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_tune) {		ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;		s->b.set_tune = 0;		if (tuneproc != NULL)			tuneproc(drive, drive->tune_req);	} else if (drive->driver != NULL) {		return DRIVER(drive)->special(drive);	} else if (s->all) {		printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);		s->all = 0;	}	return ide_stopped;}/* * 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_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) {	byte stat;	int i;	unsigned long flags; 	/* bail early if we've exceeded max_failures */	if (drive->max_failures && (drive->failures > drive->max_failures)) {		*startstop = ide_stopped;		return 1;	}	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */	if ((stat = GET_STAT()) & BUSY_STAT) {		__save_flags(flags);	/* local CPU only */		ide__sti();		/* local CPU only */		timeout += jiffies;		while ((stat = GET_STAT()) & BUSY_STAT) {			if (0 < (signed long)(jiffies - timeout)) {				__restore_flags(flags);	/* local CPU only */				*startstop = ide_error(drive, "status timeout", stat);				return 1;			}		}		__restore_flags(flags);	/* local CPU only */	}	/*	 * Allow status to settle, then read it again.	 * A few rare drives vastly violate the 400ns spec here,	 * so we'll wait up to 10usec for a "good" status	 * rather than expensively fail things immediately.	 * This fix courtesy of Matthew Faupel & Niccolo Rigacci.	 */	for (i = 0; i < 10; i++) {		udelay(1);		if (OK_STAT((stat = GET_STAT()), good, bad))			return 0;	}	*startstop = ide_error(drive, "status error", stat);	return 1;}/* * execute_drive_cmd() issues a special drive command, * usually initiated by ioctl() from the external hdparm program. */static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq){	byte *args = rq->buffer;	if (args && rq->cmd == IDE_DRIVE_TASK) {		byte sel;#ifdef DEBUG		printk("%s: DRIVE_TASK_CMD data=x%02x cmd=0x%02x fr=0x%02x ns=0x%02x sc=0x%02x lcyl=0x%02x hcyl=0x%02x sel=0x%02x\n",			drive->name,			args[0], args[1], args[2], args[3],			args[4], args[5], args[6], args[7]);#endif		OUT_BYTE(args[1], IDE_FEATURE_REG);		OUT_BYTE(args[3], IDE_SECTOR_REG);		OUT_BYTE(args[4], IDE_LCYL_REG);		OUT_BYTE(args[5], IDE_HCYL_REG);		sel = (args[6] & ~0x10);		if (drive->select.b.unit)			sel |= 0x10;		OUT_BYTE(sel, IDE_SELECT_REG);		ide_cmd(drive, args[0], args[2], &drive_cmd_intr);		return ide_started;	} else 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		if (args[0] == WIN_SMART) {			OUT_BYTE(0x4f, IDE_LCYL_REG);			OUT_BYTE(0xc2, IDE_HCYL_REG);			OUT_BYTE(args[2],IDE_FEATURE_REG);			OUT_BYTE(args[1],IDE_SECTOR_REG);			ide_cmd(drive, args[0], args[3], &drive_cmd_intr);			return ide_started;		}		OUT_BYTE(args[2],IDE_FEATURE_REG);		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);		return ide_started;	} 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 ide_stopped;	}}/* * start_request() initiates handling of a new I/O request */static ide_startstop_t start_request (ide_drive_t *drive){	ide_startstop_t startstop;	unsigned long block, blockend;	struct request *rq = blkdev_entry_next_request(&drive->queue.queue_head);	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;	ide_hwif_t *hwif = HWIF(drive);#ifdef DEBUG	printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);#endif	/* bail early if we've exceeded max_failures */	if (drive->max_failures && (drive->failures > drive->max_failures)) {		goto kill_rq;	}	if (unit >= MAX_DRIVES) {		printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev));		goto kill_rq;	}#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 ((rq->cmd == READ || rq->cmd == WRITE) &&	    (drive->media == ide_disk || drive->media == ide_floppy)) {		if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {			printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,			 (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);			goto kill_rq;		}		block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;	}	/* Yecch - this will shift the entire interval,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -