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

📄 fdc-io.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (C) 1993-1996 Bas Laarhoven, *           (C) 1996-1997 Claus-Justus Heine. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING.  If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.c,v $ * $Revision: 1.7.4.2 $ * $Date: 1997/11/16 14:48:17 $ * *      This file contains the low-level floppy disk interface code *      for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for *      Linux. */#include <linux/config.h> /* for CONFIG_FT_* */#include <linux/errno.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <asm/system.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <linux/ftape.h>#include <linux/qic117.h>#include "../lowlevel/ftape-tracing.h"#include "../lowlevel/fdc-io.h"#include "../lowlevel/fdc-isr.h"#include "../lowlevel/ftape-io.h"#include "../lowlevel/ftape-rw.h"#include "../lowlevel/ftape-ctl.h"#include "../lowlevel/ftape-calibr.h"#include "../lowlevel/fc-10.h"/*      Global vars. */static int ftape_motor;volatile int ftape_current_cylinder = -1;volatile fdc_mode_enum fdc_mode = fdc_idle;fdc_config_info fdc;DECLARE_WAIT_QUEUE_HEAD(ftape_wait_intr);unsigned int ft_fdc_base       = CONFIG_FT_FDC_BASE;unsigned int ft_fdc_irq        = CONFIG_FT_FDC_IRQ;unsigned int ft_fdc_dma        = CONFIG_FT_FDC_DMA;unsigned int ft_fdc_threshold  = CONFIG_FT_FDC_THR;  /* bytes */unsigned int ft_fdc_rate_limit = CONFIG_FT_FDC_MAX_RATE; /* bits/sec */int ft_probe_fc10        = CONFIG_FT_PROBE_FC10;int ft_mach2             = CONFIG_FT_MACH2;/*      Local vars. */static spinlock_t fdc_io_lock; static unsigned int fdc_calibr_count;static unsigned int fdc_calibr_time;static int fdc_status;volatile __u8 fdc_head;		/* FDC head from sector id */volatile __u8 fdc_cyl;		/* FDC track from sector id */volatile __u8 fdc_sect;		/* FDC sector from sector id */static int fdc_data_rate = 500;	/* data rate (Kbps) */static int fdc_rate_code;	/* data rate code (0 == 500 Kbps) */static int fdc_seek_rate = 2;	/* step rate (msec) */static void (*do_ftape) (void);static int fdc_fifo_state;	/* original fifo setting - fifo enabled */static int fdc_fifo_thr;	/* original fifo setting - threshold */static int fdc_lock_state;	/* original lock setting - locked */static int fdc_fifo_locked;	/* has fifo && lock set ? */static __u8 fdc_precomp;	/* default precomp. value (nsec) */static __u8 fdc_prec_code;	/* fdc precomp. select code */static char ftape_id[] = "ftape";  /* used by request irq and free irq */static int fdc_set_seek_rate(int seek_rate);void fdc_catch_stray_interrupts(int count){	unsigned long flags;	spin_lock_irqsave(&fdc_io_lock, flags);	if (count == 0) {		ft_expected_stray_interrupts = 0;	} else {		ft_expected_stray_interrupts += count;	}	spin_unlock_irqrestore(&fdc_io_lock, flags);}/*  Wait during a timeout period for a given FDC status. *  If usecs == 0 then just test status, else wait at least for usecs. *  Returns -ETIME on timeout. Function must be calibrated first ! */static int fdc_wait(unsigned int usecs, __u8 mask, __u8 state){	int count_1 = (fdc_calibr_count * usecs +                       fdc_calibr_count - 1) / fdc_calibr_time;	do {		fdc_status = inb_p(fdc.msr);		if ((fdc_status & mask) == state) {			return 0;		}	} while (count_1-- >= 0);	return -ETIME;}int fdc_ready_wait(unsigned int usecs){	return fdc_wait(usecs, FDC_DATA_READY | FDC_BUSY, FDC_DATA_READY);}/* Why can't we just use udelay()? */static void fdc_usec_wait(unsigned int usecs){	fdc_wait(usecs, 0, 1);	/* will always timeout ! */}static int fdc_ready_out_wait(unsigned int usecs){	fdc_usec_wait(FT_RQM_DELAY);	/* wait for valid RQM status */	return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);}void fdc_wait_calibrate(void){	ftape_calibrate("fdc_wait",			fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time); }/*  Wait for a (short) while for the FDC to become ready *  and transfer the next command byte. *  Return -ETIME on timeout on getting ready (depends on hardware!). */static int fdc_write(const __u8 data){	fdc_usec_wait(FT_RQM_DELAY);	/* wait for valid RQM status */	if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {		return -ETIME;	} else {		outb(data, fdc.fifo);		return 0;	}}/*  Wait for a (short) while for the FDC to become ready *  and transfer the next result byte. *  Return -ETIME if timeout on getting ready (depends on hardware!). */static int fdc_read(__u8 * data){	fdc_usec_wait(FT_RQM_DELAY);	/* wait for valid RQM status */	if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {		return -ETIME;	} else {		*data = inb(fdc.fifo);		return 0;	}}/*  Output a cmd_len long command string to the FDC. *  The FDC should be ready to receive a new command or *  an error (EBUSY or ETIME) will occur. */int fdc_command(const __u8 * cmd_data, int cmd_len){	int result = 0;	unsigned long flags;	int count = cmd_len;	int retry = 0;#ifdef TESTING	static unsigned int last_time;	unsigned int time;#endif	TRACE_FUN(ft_t_any);	fdc_usec_wait(FT_RQM_DELAY);	/* wait for valid RQM status */	spin_lock_irqsave(&fdc_io_lock, flags);	if (!in_interrupt())		/* Yes, I know, too much comments inside this function		 * ...		 * 		 * Yet another bug in the original driver. All that		 * havoc is caused by the fact that the isr() sends		 * itself a command to the floppy tape driver (pause,		 * micro step pause).  Now, the problem is that		 * commands are transmitted via the fdc_seek		 * command. But: the fdc performs seeks in the		 * background i.e. it doesn't signal busy while		 * sending the step pulses to the drive. Therefore the		 * non-interrupt level driver has no chance to tell		 * whether the isr() just has issued a seek. Therefore		 * we HAVE TO have a look at the ft_hide_interrupt		 * flag: it signals the non-interrupt level part of		 * the driver that it has to wait for the fdc until it		 * has completet seeking.		 *		 * THIS WAS PRESUMABLY THE REASON FOR ALL THAT		 * "fdc_read timeout" errors, I HOPE :-)		 */		if (ft_hide_interrupt) {			restore_flags(flags);			TRACE(ft_t_info,			      "Waiting for the isr() completing fdc_seek()");			if (fdc_interrupt_wait(2 * FT_SECOND) < 0) {				TRACE(ft_t_warn,		      "Warning: timeout waiting for isr() seek to complete");			}			if (ft_hide_interrupt || !ft_seek_completed) {				/* There cannot be another				 * interrupt. The isr() only stops				 * the tape and the next interrupt				 * won't come until we have send our				 * command to the drive.				 */				TRACE_ABORT(-EIO, ft_t_bug,					    "BUG? isr() is still seeking?\n"					    KERN_INFO "hide: %d\n"					    KERN_INFO "seek: %d",					    ft_hide_interrupt,					    ft_seek_completed);			}			fdc_usec_wait(FT_RQM_DELAY);	/* wait for valid RQM status */			spin_lock_irqsave(&fdc_io_lock, flags);		}	fdc_status = inb(fdc.msr);	if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) {		spin_unlock_irqrestore(&fdc_io_lock, flags);		TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready");	} 	fdc_mode = *cmd_data;	/* used by isr */#ifdef TESTING	if (fdc_mode == FDC_SEEK) {		time = ftape_timediff(last_time, ftape_timestamp());		if (time < 6000) {	TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d",	      time);		}	}#endif	if (!in_interrupt()) {		/* shouldn't be cleared if called from isr		 */		ft_interrupt_seen = 0;	}	while (count) {		result = fdc_write(*cmd_data);		if (result < 0) {			TRACE(ft_t_fdc_dma,			      "fdc_mode = %02x, status = %02x at index %d",			      (int) fdc_mode, (int) fdc_status,			      cmd_len - count);			if (++retry <= 3) {				TRACE(ft_t_warn, "fdc_write timeout, retry");			} else {				TRACE(ft_t_err, "fdc_write timeout, fatal");				/* recover ??? */				break;			}		} else {			--count;			++cmd_data;		}        }#ifdef TESTING	if (fdc_mode == FDC_SEEK) {		last_time = ftape_timestamp();	}#endif	spin_unlock_irqrestore(&fdc_io_lock, flags);	TRACE_EXIT result;}/*  Input a res_len long result string from the FDC. *  The FDC should be ready to send the result or an error *  (EBUSY or ETIME) will occur. */int fdc_result(__u8 * res_data, int res_len){	int result = 0;	unsigned long flags;	int count = res_len;	int retry = 0;	TRACE_FUN(ft_t_any);	spin_lock_irqsave(&fdc_io_lock, flags);	fdc_status = inb(fdc.msr);	if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_OUT_READY) {		TRACE(ft_t_err, "fdc not ready");		result = -EBUSY;	} else while (count) {		if (!(fdc_status & FDC_BUSY)) {			spin_unlock_irqrestore(&fdc_io_lock, flags);			TRACE_ABORT(-EIO, ft_t_err, "premature end of result phase");		}		result = fdc_read(res_data);		if (result < 0) {			TRACE(ft_t_fdc_dma,			      "fdc_mode = %02x, status = %02x at index %d",			      (int) fdc_mode,			      (int) fdc_status,			      res_len - count);			if (++retry <= 3) {				TRACE(ft_t_warn, "fdc_read timeout, retry");			} else {				TRACE(ft_t_err, "fdc_read timeout, fatal");				/* recover ??? */				break;				++retry;			}		} else {			--count;			++res_data;		}	}	spin_unlock_irqrestore(&fdc_io_lock, flags);	fdc_usec_wait(FT_RQM_DELAY);	/* allow FDC to negate BSY */	TRACE_EXIT result;}/*      Handle command and result phases for *      commands without data phase. */static int fdc_issue_command(const __u8 * out_data, int out_count,		      __u8 * in_data, int in_count){	TRACE_FUN(ft_t_any);	if (out_count > 0) {		TRACE_CATCH(fdc_command(out_data, out_count),);	}	/* will take 24 - 30 usec for fdc_sense_drive_status and	 * fdc_sense_interrupt_status commands.	 *    35 fails sometimes (5/9/93 SJL)	 * On a loaded system it incidentally takes longer than	 * this for the fdc to get ready ! ?????? WHY ??????	 * So until we know what's going on use a very long timeout.	 */	TRACE_CATCH(fdc_ready_out_wait(500 /* usec */),);	if (in_count > 0) {		TRACE_CATCH(fdc_result(in_data, in_count),			    TRACE(ft_t_err, "result phase aborted"));	}	TRACE_EXIT 0;}/*      Wait for FDC interrupt with timeout (in milliseconds). *      Signals are blocked so the wait will not be aborted. *      Note: interrupts must be enabled ! (23/05/93 SJL) */int fdc_interrupt_wait(unsigned int time){	DECLARE_WAITQUEUE(wait,current);	sigset_t old_sigmask;		static int resetting;	long timeout;	TRACE_FUN(ft_t_fdc_dma); 	if (waitqueue_active(&ftape_wait_intr)) {		TRACE_ABORT(-EIO, ft_t_err, "error: nested call");	}	/* timeout time will be up to USPT microseconds too long ! */	timeout = (1000 * time + FT_USPT - 1) / FT_USPT;	spin_lock_irq(&current->sighand->siglock);	old_sigmask = current->blocked;	sigfillset(&current->blocked);	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);	set_current_state(TASK_INTERRUPTIBLE);	add_wait_queue(&ftape_wait_intr, &wait);	while (!ft_interrupt_seen && timeout)		timeout = schedule_timeout_interruptible(timeout);	spin_lock_irq(&current->sighand->siglock);	current->blocked = old_sigmask;	recalc_sigpending();	spin_unlock_irq(&current->sighand->siglock);		remove_wait_queue(&ftape_wait_intr, &wait);	/*  the following IS necessary. True: as well	 *  wake_up_interruptible() as the schedule() set TASK_RUNNING	 *  when they wakeup a task, BUT: it may very well be that	 *  ft_interrupt_seen is already set to 1 when we enter here	 *  in which case schedule() gets never called, and	 *  TASK_RUNNING never set. This has the funny effect that we	 *  execute all the code until we leave kernel space, but then	 *  the task is stopped (a task CANNOT be preempted while in	 *  kernel mode. Sending a pair of SIGSTOP/SIGCONT to the	 *  tasks wakes it up again. Funny! :-)	 */	current->state = TASK_RUNNING; 	if (ft_interrupt_seen) { /* woken up by interrupt */		ft_interrupt_seen = 0;		TRACE_EXIT 0;	}	/*  Original comment:	 *  In first instance, next statement seems unnecessary since	 *  it will be cleared in fdc_command. However, a small part of	 *  the software seems to rely on this being cleared here	 *  (ftape_close might fail) so stick to it until things get fixed !	 */	/*  My deeply sought of knowledge:	 *  Behold NO! It is obvious. fdc_reset() doesn't call fdc_command()	 *  but nevertheless uses fdc_interrupt_wait(). OF COURSE this needs to	 *  be reset here.	 */	ft_interrupt_seen = 0;	/* clear for next call */	if (!resetting) {		resetting = 1;	/* break infinite recursion if reset fails */		TRACE(ft_t_any, "cleanup reset");		fdc_reset();		resetting = 0;	}	TRACE_EXIT (signal_pending(current)) ? -EINTR : -ETIME;}/*      Start/stop drive motor. Enable DMA mode. */void fdc_motor(int motor){	int unit = ft_drive_sel;	int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;	TRACE_FUN(ft_t_any);	ftape_motor = motor;	if (ftape_motor) {		data |= FDC_MOTOR_0 << unit;		TRACE(ft_t_noise, "turning motor %d on", unit);	} else {		TRACE(ft_t_noise, "turning motor %d off", unit);	}	if (ft_mach2) {

⌨️ 快捷键说明

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