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

📄 floppy.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	clear_dma_ff(FLOPPY_DMA);
	set_dma_mode(FLOPPY_DMA, (command == FD_READ)? DMA_MODE_READ : DMA_MODE_WRITE);
	set_dma_addr(FLOPPY_DMA, addr);
	set_dma_count(FLOPPY_DMA, count);
	enable_dma(FLOPPY_DMA);
	sti();
}

static void output_byte(char byte)
{
	int counter;
	unsigned char status;

	if (reset)
		return;
	for(counter = 0 ; counter < 10000 ; counter++) {
		status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR);
		if (status == STATUS_READY) {
			outb(byte,FD_DATA);
			return;
		}
	}
	current_track = NO_TRACK;
	reset = 1;
	printk("Unable to send byte to FDC\n");
}

static int result(void)
{
	int i = 0, counter, status;

	if (reset)
		return -1;
	for (counter = 0 ; counter < 10000 ; counter++) {
		status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
		if (status == STATUS_READY) {
			return i;
		}
		if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
			if (i >= MAX_REPLIES) {
				printk("floppy_stat reply overrun\n");
				break;
			}
			reply_buffer[i++] = inb_p(FD_DATA);
		}
	}
	reset = 1;
	current_track = NO_TRACK;
	printk("Getstatus times out\n");
	return -1;
}

static void bad_flp_intr(void)
{
	int errors;

	current_track = NO_TRACK;
	if (format_status == FORMAT_BUSY)
		errors = ++format_errors;
	else if (!CURRENT) {
		printk(DEVICE_NAME ": no current request\n");
		reset = recalibrate = 1;
		return;
	} else
		errors = ++CURRENT->errors;
	if (errors > MAX_ERRORS) {
		request_done(0);
	}
	if (errors > MAX_ERRORS/2)
		reset = 1;
	else
		recalibrate = 1;
}	


/* Set perpendicular mode as required, based on data rate, if supported.
 * 82077 Untested! 1Mbps data rate only possible with 82077-1.
 * TODO: increase MAX_BUFFER_SECTORS, add floppy_type entries.
 */
static inline void perpendicular_mode(unsigned char rate)
{
	if (fdc_version == FDC_TYPE_82077) {
		output_byte(FD_PERPENDICULAR);
		if (rate & 0x40) {
			unsigned char r = rate & 0x03;
			if (r == 0)
				output_byte(2);	/* perpendicular, 500 kbps */
			else if (r == 3)
				output_byte(3);	/* perpendicular, 1Mbps */
			else {
				printk(DEVICE_NAME ": Invalid data rate for perpendicular mode!\n");
				reset = 1;
			}
		} else
			output_byte(0);		/* conventional mode */
	} else {
		if (rate & 0x40) {
			printk(DEVICE_NAME ": perpendicular mode not supported by this FDC.\n");
			reset = 1;
		}
	}
} /* perpendicular_mode */


/*
 * This has only been tested for the case fdc_version == FDC_TYPE_STD.
 * In case you have a 82077 and want to test it, you'll have to compile
 * with `FDC_FIFO_UNTESTED' defined. You may also want to add support for
 * recognizing drives with vertical recording support.
 */
static void configure_fdc_mode(void)
{
	if (need_configure && (fdc_version == FDC_TYPE_82077)) {
		/* Enhanced version with FIFO & vertical recording. */
		output_byte(FD_CONFIGURE);
		output_byte(0);
		output_byte(0x1A);	/* FIFO on, polling off, 10 byte threshold */
		output_byte(0);		/* precompensation from track 0 upwards */
		need_configure = 0;
		printk(DEVICE_NAME ": FIFO enabled\n");
	}
	if (cur_spec1 != floppy->spec1) {
		cur_spec1 = floppy->spec1;
		output_byte(FD_SPECIFY);
		output_byte(cur_spec1);		/* hut etc */
		output_byte(6);			/* Head load time =6ms, DMA */
	}
	if (cur_rate != floppy->rate) {
		/* use bit 6 of floppy->rate to indicate perpendicular mode */
		perpendicular_mode(floppy->rate);
		outb_p((cur_rate = (floppy->rate)) & ~0x40, FD_DCR);
	}
} /* configure_fdc_mode */


static void tell_sector(int nr)
{
	if (nr!=7) {
		printk(" -- FDC reply errror");
		reset = 1;
	} else
		printk(": track %d, head %d, sector %d", reply_buffer[3],
			reply_buffer[4], reply_buffer[5]);
} /* tell_sector */


/*
 * Ok, this interrupt is called after a DMA read/write has succeeded
 * or failed, so we check the results, and copy any buffers.
 * hhb: Added better error reporting.
 */
static void rw_interrupt(void)
{
	char * buffer_area;
	int nr;
	char bad;

	nr = result();
	/* check IC to find cause of interrupt */
	switch ((ST0 & ST0_INTR)>>6) {
		case 1:	/* error occured during command execution */
			bad = 1;
			if (ST1 & ST1_WP) {
				printk(DEVICE_NAME ": Drive %d is write protected\n", current_drive);
				request_done(0);
				bad = 0;
			} else if (ST1 & ST1_OR) {
				if (ftd_msg[ST0 & ST0_DS])
					printk(DEVICE_NAME ": Over/Underrun - retrying\n");
				/* could continue from where we stopped, but ... */
				bad = 0;
			} else if (CURRENT_ERRORS > min_report_error_cnt[ST0 & ST0_DS]) {
				printk(DEVICE_NAME " %d: ", ST0 & ST0_DS);
				if (ST0 & ST0_ECE) {
					printk("Recalibrate failed!");
				} else if (ST2 & ST2_CRC) {
					printk("data CRC error");
					tell_sector(nr);
				} else if (ST1 & ST1_CRC) {
					printk("CRC error");
					tell_sector(nr);
				} else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
					if (!probing) {
						printk("sector not found");
						tell_sector(nr);
					} else
						printk("probe failed...");
				} else if (ST2 & ST2_WC) {	/* seek error */
					printk("wrong cylinder");
				} else if (ST2 & ST2_BC) {	/* cylinder marked as bad */
					printk("bad cylinder");
				} else {
					printk("unknown error. ST[0..3] are: 0x%x 0x%x 0x%x 0x%x\n", ST0, ST1, ST2, ST3);
				}
				printk("\n");

			}
			if (bad)
				bad_flp_intr();
			redo_fd_request();
			return;
		case 2: /* invalid command given */
			printk(DEVICE_NAME ": Invalid FDC command given!\n");
			request_done(0);
			return;
		case 3:
			printk(DEVICE_NAME ": Abnormal termination caused by polling\n");
			bad_flp_intr();
			redo_fd_request();
			return;
		default: /* (0) Normal command termination */
			break;
	}

	if (probing) {
		int drive = MINOR(CURRENT->dev);

		if (ftd_msg[drive])
			printk("Auto-detected floppy type %s in fd%d\n",
			    floppy->name,drive);
		current_type[drive] = floppy;
		floppy_sizes[drive] = floppy->size >> 1;
		probing = 0;
	}
	if (read_track) {
		buffer_track = seek_track;
		buffer_drive = current_drive;
		buffer_area = floppy_track_buffer +
			((sector-1 + head*floppy->sect)<<9);
		copy_buffer(buffer_area,CURRENT->buffer);
	} else if (command == FD_READ &&
		(unsigned long)(CURRENT->buffer) >= LAST_DMA_ADDR)
		copy_buffer(tmp_floppy_area,CURRENT->buffer);
	request_done(1);
	redo_fd_request();
}

/*
 * We try to read tracks, but if we get too many errors, we
 * go back to reading just one sector at a time.
 *
 * This means we should be able to read a sector even if there
 * are other bad sectors on this track.
 */
inline void setup_rw_floppy(void)
{
	setup_DMA();
	do_floppy = rw_interrupt;
	output_byte(command);
	if (command != FD_FORMAT) {
		if (read_track) {
			output_byte(current_drive);
			output_byte(track);
			output_byte(0);
			output_byte(1);
		} else {
			output_byte(head<<2 | current_drive);
			output_byte(track);
			output_byte(head);
			output_byte(sector);
		}
		output_byte(2);		/* sector size = 512 */
		output_byte(floppy->sect);
		output_byte(floppy->gap);
		output_byte(0xFF);	/* sector size (0xff when n!=0 ?) */
	} else {
		output_byte(head<<2 | current_drive);
		output_byte(2);
		output_byte(floppy->sect);
		output_byte(floppy->fmt_gap);
		output_byte(FD_FILL_BYTE);
	}
	if (reset)
		redo_fd_request();
}

/*
 * This is the routine called after every seek (or recalibrate) interrupt
 * from the floppy controller. Note that the "unexpected interrupt" routine
 * also does a recalibrate, but doesn't come here.
 */
static void seek_interrupt(void)
{
/* sense drive status */
	output_byte(FD_SENSEI);
	if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) {
		printk(DEVICE_NAME ": seek failed\n");
		recalibrate = 1;
		bad_flp_intr();
		redo_fd_request();
		return;
	}
	current_track = ST1;
	setup_rw_floppy();
}


/*
 * This routine is called when everything should be correctly set up
 * for the transfer (ie floppy motor is on and the correct floppy is
 * selected).
 */
static void transfer(void)
{
	read_track = (command == FD_READ) && (CURRENT_ERRORS < 4) &&
	    (floppy->sect <= MAX_BUFFER_SECTORS);

	configure_fdc_mode();

	if (reset) {
		redo_fd_request();
		return;
	}
	if (!seek) {
		setup_rw_floppy();
		return;
	}

	do_floppy = seek_interrupt;
	output_byte(FD_SEEK);
	if (read_track)
		output_byte(current_drive);
	else
		output_byte((head<<2) | current_drive);
	output_byte(seek_track);
	if (reset)
		redo_fd_request();
}

/*
 * Special case - used after a unexpected interrupt (or reset)
 */

static void recalibrate_floppy(void);

static void recal_interrupt(void)
{
	output_byte(FD_SENSEI);
	current_track = NO_TRACK;
	if (result()!=2 || (ST0 & 0xE0) == 0x60)
		reset = 1;
/* Recalibrate until track 0 is reached. Might help on some errors. */
	if ((ST0 & 0x10) == 0x10)
		recalibrate_floppy();	/* FIXME: should limit nr of recalibrates */
	else
		redo_fd_request();
}

static void unexpected_floppy_interrupt(void)
{
	current_track = NO_TRACK;
	output_byte(FD_SENSEI);
	printk(DEVICE_NAME ": unexpected interrupt\n");
	if (result()!=2 || (ST0 & 0xE0) == 0x60)
		reset = 1;
	else
		recalibrate = 1;
}

static void recalibrate_floppy(void)
{
	recalibrate = 0;
	current_track = 0;
	do_floppy = recal_interrupt;
	output_byte(FD_RECALIBRATE);
	output_byte(head<<2 | current_drive);
	if (reset)
		redo_fd_request();
}

/*
 * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
 */
static void reset_interrupt(void)
{
	short i;

	for (i=0; i<4; i++) {
		output_byte(FD_SENSEI);
		(void) result();
	}
	output_byte(FD_SPECIFY);
	output_byte(cur_spec1);		/* hut etc */
	output_byte(6);			/* Head load time =6ms, DMA */
	configure_fdc_mode();		/* reprogram fdc */
	if (initial_reset_flag) {
		initial_reset_flag = 0;
		recalibrate = 1;
		reset = 0;
		return;
	}
	if (!recover)
		redo_fd_request();
	else {
		recalibrate_floppy();
		recover = 0;
	}
}

/*
 * reset is done by pulling bit 2 of DOR low for a while.
 */
static void reset_floppy(void)
{
	int i;

	do_floppy = reset_interrupt;
	reset = 0;
	current_track = NO_TRACK;
	cur_spec1 = -1;
	cur_rate = -1;
	recalibrate = 1;
	need_configure = 1;
	if (!initial_reset_flag)
		printk("Reset-floppy called\n");
	cli();
	outb_p(current_DOR & ~0x04, FD_DOR);
	for (i=0 ; i<1000 ; i++)
		__asm__("nop");
	outb(current_DOR, FD_DOR);
	sti();
}

static void floppy_shutdown(void)
{
	cli();
	do_floppy = NULL;
	request_done(0);
	recover = 1;
	reset_floppy();
	sti();
	redo_fd_request();
}

static void shake_done(void)
{
	current_track = NO_TRACK;
	if (inb(FD_DIR) & 0x80)
		request_done(0);
	redo_fd_request();
}

static int retry_recal(void (*proc)(void))
{
	output_byte(FD_SENSEI);
	if (result() == 2 && (ST0 & 0x10) != 0x10) return 0;
	do_floppy = proc;
	output_byte(FD_RECALIBRATE);
	output_byte(head<<2 | current_drive);
	return 1;
}

static void shake_zero(void)
{
	if (!retry_recal(shake_zero)) shake_done();
}

static void shake_one(void)
{
	if (retry_recal(shake_one)) return;
	do_floppy = shake_done;
	output_byte(FD_SEEK);
	output_byte(head << 2 | current_drive);

⌨️ 快捷键说明

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