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

📄 floppy.c

📁 Linux块设备驱动源码
💻 C
📖 第 1 页 / 共 5 页
字号:
				if (!probing) {					printk("sector not found");					tell_sector();				} 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..2] are: 0x%x 0x%x 0x%x",				     ST0, ST1, ST2);				tell_sector();			}			printk("\n");		}		if (ST2 & ST2_WC || ST2 & ST2_BC)			/* wrong cylinder => recal */			DRS->track = NEED_2_RECAL;		return bad;	case 0x80:		/* invalid command given */		DPRINT("Invalid FDC command given!\n");		cont->done(0);		return 2;	case 0xc0:		DPRINT("Abnormal termination caused by polling\n");		cont->error();		return 2;	default:		/* (0) Normal command termination */		return 0;	}}/* * This routine is called when everything should be correctly set up * for the transfer (i.e. floppy motor is on, the correct floppy is * selected, and the head is sitting on the right track). */static void setup_rw_floppy(void){	int i, r, flags, dflags;	unsigned long ready_date;	timeout_fn function;	flags = raw_cmd->flags;	if (flags & (FD_RAW_READ | FD_RAW_WRITE))		flags |= FD_RAW_INTR;	if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) {		ready_date = DRS->spinup_date + DP->spinup;		/* If spinup will take a long time, rerun scandrives		 * again just before spinup completion. Beware that		 * after scandrives, we must again wait for selection.		 */		if ((signed)(ready_date - jiffies) > DP->select_delay) {			ready_date -= DP->select_delay;			function = (timeout_fn) floppy_start;		} else			function = (timeout_fn) setup_rw_floppy;		/* wait until the floppy is spinning fast enough */		if (fd_wait_for_completion(ready_date, function))			return;	}	dflags = DRS->flags;	if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))		setup_DMA();	if (flags & FD_RAW_INTR)		do_floppy = main_command_interrupt;	r = 0;	for (i = 0; i < raw_cmd->cmd_count; i++)		r |= output_byte(raw_cmd->cmd[i]);	debugt("rw_command: ");	if (r) {		cont->error();		reset_fdc();		return;	}	if (!(flags & FD_RAW_INTR)) {		inr = result();		cont->interrupt();	} else if (flags & FD_RAW_NEED_DISK)		fd_watchdog();}static int blind_seek;/* * This is the routine called after every seek (or recalibrate) interrupt * from the floppy controller. */static void seek_interrupt(void){	debugt("seek interrupt:");	if (inr != 2 || (ST0 & 0xF8) != 0x20) {		DPRINT("seek failed\n");		DRS->track = NEED_2_RECAL;		cont->error();		cont->redo();		return;	}	if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek) {#ifdef DCL_DEBUG		if (DP->flags & FD_DEBUG) {			DPRINT			    ("clearing NEWCHANGE flag because of effective seek\n");			DPRINT("jiffies=%lu\n", jiffies);		}#endif		CLEARF(FD_DISK_NEWCHANGE);	/* effective seek */		DRS->select_date = jiffies;	}	DRS->track = ST1;	floppy_ready();}static void check_wp(void){	if (TESTF(FD_VERIFY)) {		/* check write protection */		output_byte(FD_GETSTATUS);		output_byte(UNIT(current_drive));		if (result() != 1) {			FDCS->reset = 1;			return;		}		CLEARF(FD_VERIFY);		CLEARF(FD_NEED_TWADDLE);#ifdef DCL_DEBUG		if (DP->flags & FD_DEBUG) {			DPRINT("checking whether disk is write protected\n");			DPRINT("wp=%x\n", ST3 & 0x40);		}#endif		if (!(ST3 & 0x40))			SETF(FD_DISK_WRITABLE);		else			CLEARF(FD_DISK_WRITABLE);	}}static void seek_floppy(void){	int track;	blind_seek = 0;#ifdef DCL_DEBUG	if (DP->flags & FD_DEBUG) {		DPRINT("calling disk change from seek\n");	}#endif	if (!TESTF(FD_DISK_NEWCHANGE) &&	    disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) {		/* the media changed flag should be cleared after the seek.		 * If it isn't, this means that there is really no disk in		 * the drive.		 */		SETF(FD_DISK_CHANGED);		cont->done(0);		cont->redo();		return;	}	if (DRS->track <= NEED_1_RECAL) {		recalibrate_floppy();		return;	} else if (TESTF(FD_DISK_NEWCHANGE) &&		   (raw_cmd->flags & FD_RAW_NEED_DISK) &&		   (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {		/* we seek to clear the media-changed condition. Does anybody		 * know a more elegant way, which works on all drives? */		if (raw_cmd->track)			track = raw_cmd->track - 1;		else {			if (DP->flags & FD_SILENT_DCL_CLEAR) {				set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0);				blind_seek = 1;				raw_cmd->flags |= FD_RAW_NEED_SEEK;			}			track = 1;		}	} else {		check_wp();		if (raw_cmd->track != DRS->track &&		    (raw_cmd->flags & FD_RAW_NEED_SEEK))			track = raw_cmd->track;		else {			setup_rw_floppy();			return;		}	}	do_floppy = seek_interrupt;	output_byte(FD_SEEK);	output_byte(UNIT(current_drive));	LAST_OUT(track);	debugt("seek command:");}static void recal_interrupt(void){	debugt("recal interrupt:");	if (inr != 2)		FDCS->reset = 1;	else if (ST0 & ST0_ECE) {		switch (DRS->track) {		case NEED_1_RECAL:			debugt("recal interrupt need 1 recal:");			/* after a second recalibrate, we still haven't			 * reached track 0. Probably no drive. Raise an			 * error, as failing immediately might upset			 * computers possessed by the Devil :-) */			cont->error();			cont->redo();			return;		case NEED_2_RECAL:			debugt("recal interrupt need 2 recal:");			/* If we already did a recalibrate,			 * and we are not at track 0, this			 * means we have moved. (The only way			 * not to move at recalibration is to			 * be already at track 0.) Clear the			 * new change flag */#ifdef DCL_DEBUG			if (DP->flags & FD_DEBUG) {				DPRINT				    ("clearing NEWCHANGE flag because of second recalibrate\n");			}#endif			CLEARF(FD_DISK_NEWCHANGE);			DRS->select_date = jiffies;			/* fall through */		default:			debugt("recal interrupt default:");			/* Recalibrate moves the head by at			 * most 80 steps. If after one			 * recalibrate we don't have reached			 * track 0, this might mean that we			 * started beyond track 80.  Try			 * again.  */			DRS->track = NEED_1_RECAL;			break;		}	} else		DRS->track = ST1;	floppy_ready();}static void print_result(char *message, int inr){	int i;	DPRINT("%s ", message);	if (inr >= 0)		for (i = 0; i < inr; i++)			printk("repl[%d]=%x ", i, reply_buffer[i]);	printk("\n");}/* interrupt handler. Note that this can be called externally on the Sparc */irqreturn_t floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs){	void (*handler) (void) = do_floppy;	int do_print;	unsigned long f;	lasthandler = handler;	interruptjiffies = jiffies;	f = claim_dma_lock();	fd_disable_dma();	release_dma_lock(f);	floppy_enable_hlt();	do_floppy = NULL;	if (fdc >= N_FDC || FDCS->address == -1) {		/* we don't even know which FDC is the culprit */		printk("DOR0=%x\n", fdc_state[0].dor);		printk("floppy interrupt on bizarre fdc %d\n", fdc);		printk("handler=%p\n", handler);		is_alive("bizarre fdc");		return IRQ_NONE;	}	FDCS->reset = 0;	/* We have to clear the reset flag here, because apparently on boxes	 * with level triggered interrupts (PS/2, Sparc, ...), it is needed to	 * emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the	 * emission of the SENSEI's.	 * It is OK to emit floppy commands because we are in an interrupt	 * handler here, and thus we have to fear no interference of other	 * activity.	 */	do_print = !handler && print_unex && !initialising;	inr = result();	if (do_print)		print_result("unexpected interrupt", inr);	if (inr == 0) {		int max_sensei = 4;		do {			output_byte(FD_SENSEI);			inr = result();			if (do_print)				print_result("sensei", inr);			max_sensei--;		} while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2			 && max_sensei);	}	if (!handler) {		FDCS->reset = 1;		return IRQ_NONE;	}	schedule_bh(handler);	is_alive("normal interrupt end");	/* FIXME! Was it really for us? */	return IRQ_HANDLED;}static void recalibrate_floppy(void){	debugt("recalibrate floppy:");	do_floppy = recal_interrupt;	output_byte(FD_RECALIBRATE);	LAST_OUT(UNIT(current_drive));}/* * Must do 4 FD_SENSEIs after reset because of ``drive polling''. */static void reset_interrupt(void){	debugt("reset interrupt:");	result();		/* get the status ready for set_fdc */	if (FDCS->reset) {		printk("reset set in interrupt, calling %p\n", cont->error);		cont->error();	/* a reset just after a reset. BAD! */	}	cont->redo();}/* * reset is done by pulling bit 2 of DOR low for a while (old FDCs), * or by setting the self clearing bit 7 of STATUS (newer FDCs) */static void reset_fdc(void){	unsigned long flags;	do_floppy = reset_interrupt;	FDCS->reset = 0;	reset_fdc_info(0);	/* Pseudo-DMA may intercept 'reset finished' interrupt.  */	/* Irrelevant for systems with true DMA (i386).          */	flags = claim_dma_lock();	fd_disable_dma();	release_dma_lock(flags);	if (FDCS->version >= FDC_82072A)		fd_outb(0x80 | (FDCS->dtr & 3), FD_STATUS);	else {		fd_outb(FDCS->dor & ~0x04, FD_DOR);		udelay(FD_RESET_DELAY);		fd_outb(FDCS->dor, FD_DOR);	}}static void show_floppy(void){	int i;	printk("\n");	printk("floppy driver state\n");	printk("-------------------\n");	printk("now=%lu last interrupt=%lu diff=%lu last called handler=%p\n",	       jiffies, interruptjiffies, jiffies - interruptjiffies,	       lasthandler);#ifdef FLOPPY_SANITY_CHECK	printk("timeout_message=%s\n", timeout_message);	printk("last output bytes:\n");	for (i = 0; i < OLOGSIZE; i++)		printk("%2x %2x %lu\n",		       output_log[(i + output_log_pos) % OLOGSIZE].data,		       output_log[(i + output_log_pos) % OLOGSIZE].status,		       output_log[(i + output_log_pos) % OLOGSIZE].jiffies);	printk("last result at %lu\n", resultjiffies);	printk("last redo_fd_request at %lu\n", lastredo);	for (i = 0; i < resultsize; i++) {		printk("%2x ", reply_buffer[i]);	}	printk("\n");#endif	printk("status=%x\n", fd_inb(FD_STATUS));	printk("fdc_busy=%lu\n", fdc_busy);	if (do_floppy)		printk("do_floppy=%p\n", do_floppy);	if (floppy_work.pending)		printk("floppy_work.func=%p\n", floppy_work.func);	if (timer_pending(&fd_timer))		printk("fd_timer.function=%p\n", fd_timer.function);	if (timer_pending(&fd_timeout)) {		printk("timer_function=%p\n", fd_timeout.function);		printk("expires=%lu\n", fd_timeout.expires - jiffies);		printk("now=%lu\n", jiffies);	}	printk("cont=%p\n", cont);	printk("current_req=%p\n", current_req);	printk("command_status=%d\n", command_status);	printk("\n");}static void floppy_shutdown(unsigned long data){	unsigned long flags;	if (!initialising)		show_floppy();	cancel_activity();	floppy_enable_hlt();	flags = claim_dma_lock();	fd_disable_dma();	release_dma_lock(flags);	/* avoid dma going to a random drive after shutdown */	if (!initialising)		DPRINT("floppy timeout called\n");	FDCS->reset = 1;	if (cont) {		cont->done(0);		cont->redo();	/* this will recall reset when needed */	} else {		printk("no cont in shutdown!\n");		process_fd_request();	}	is_alive("floppy shutdown");}/*typedef void (*timeout_fn)(unsigned long);*//* start motor, check media-changed condition and write protection */static int start_motor(void (*function) (void)){	int mask, data;	mask = 0xfc;	data = UNIT(current_drive);	if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) {		if (!(FDCS->dor & (0x10 << UNIT(current_drive)))) {			set_debugt();			/* no read since this drive is running */			DRS->first_read_date = 0;			/* note motor start time if motor is not yet running */			DRS->spinup_date = jiffies;			data |= (0x10 << UNIT(current_drive));		}	} else if (FDCS->dor & (0x10 << UNIT(current_drive)))		mask &= ~(0x10 << UNIT(current_drive));	/* starts motor and selects floppy */	del_timer(motor_off_timer + current_drive);	set_dor(fdc, mask, data);	/* wait_for_completion also schedules reset if needed. */	return (fd_wait_for_completion(DRS->select_date + DP->select_delay,				       (timeout_fn) function));}static void floppy_ready(void){	CHECK_RESET;	if (start_motor(floppy_ready))

⌨️ 快捷键说明

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