📄 tpqic02.c
字号:
return -EACCES;
return do_qic_cmd(QCMD_RD_FM, TIM_F);
case MTBSF:
#ifdef TP_HAVE_BSF
tpqputs("MTBSF backward searching filemark -- optional command");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
#else
tpqputs("MTBSF not supported");
stat = -ENXIO;
#endif
status_eom_detected = status_eof_detected = NO;
return stat;
case MTFSR:
#ifdef TP_HAVE_FSR /* This is an optional QIC-02 command */
tpqputs("MTFSR forward space record");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
#else
/**** fake it by doing a read data block command? ******/
tpqputs("MTFSR not supported");
stat = -ENXIO;
#endif
return stat;
case MTBSR:
#ifdef TP_HAVE_BSR /* This is an optional QIC-02 command */
/* we need this for appending files with GNU tar!! */
tpqputs("MTFSR backward space record");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
#else
tpqputs("MTBSR not supported");
stat = -ENXIO;
#endif
status_eom_detected = status_eof_detected = NO;
return stat;
case MTWEOF:
tpqputs("MTWEOF write eof mark");
/* Plain GNU mt(1) 2.2 uses read-only mode for writing FM. :-( */
if (mode_access==READ)
return -EACCES;
/* allow tape movement after writing FM */
status_bytes_rd = status_bytes_wr; /* Kludge-O-Matic */
status_bytes_wr = NO;
return do_qic_cmd(QCMD_WRT_FM, TIM_M);
/* not sure what to do with status_bytes when WFM should fail */
case MTREW:
tpqputs("MTREW rewinding tape");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
status_eom_detected = status_eof_detected = NO;
return do_qic_cmd(QCMD_REWIND, TIM_R);
case MTOFFL:
tpqputs("MTOFFL rewinding & going offline"); /*---*/
/******* What exactly are we supposed to do, to take it offline????
*****/
/* Doing a drive select will clear (unlock) the current drive.
* But that requires support for multiple drives and locking.
*/
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
status_eom_detected = status_eof_detected = NO;
/**** do rewind depending on minor bits??? ***/
stat = do_qic_cmd(QCMD_REWIND, TIM_R);
return stat;
case MTNOP:
tpqputs("MTNOP setting status only");
/********** should do `read position' for drives that support it **********/
return (tp_sense(-1)==TE_OK)? 0 : -EIO; /**** check return codes ****/
case MTRETEN:
tpqputs("MTRETEN retension tape");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
status_eom_detected = status_eof_detected = NO;
return do_qic_cmd(QCMD_RETEN, TIM_R);
case MTBSFM:
/* Think think is like MTBSF, except that
* we shouldn't skip the FM. Tricky.
* Maybe use RD_FM_BCK, then do a SPACE_FWD?
*/
tpqputs("MTBSFM not supported");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return -ENXIO;
case MTFSFM:
/* I think this is like MTFSF, except that
* we shouldn't skip the FM. Tricky.
* Maybe use QCMD_RD_DATA until we get a TP_FIL exception?
* But then the FM will have been skipped...
* Maybe use RD_FM, then RD_FM_BCK, but not all
* drives will support that!
*/
tpqputs("MTFSFM not supported");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
return -ENXIO;
case MTEOM:
/* This should leave the tape ready for appending
* another file to the end, such that it would append
* after the last FM on tape.
*/
tpqputs("MTEOM search for End Of recorded Media");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
#ifdef TP_HAVE_EOD
/* Use faster seeking when possible.
* This requires the absence of data beyond the EOM.
*/
# if TAPE_QIC02_DRIVE == MT_ISWT5150
/* It seems that my drive does not always perform the
* SEEK_EOD correctly, unless it is preceded by a
* rewind command.
*/
# if 0
status_eom_detected = status_eof_detected = NO;
# endif
stat = do_qic_cmd(QCMD_REWIND, TIM_R);
if (stat)
return stat;
# endif
stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
/* After a successful seek, TP_EOR should be returned */
#else
/* else just seek until the drive returns exception "No Data" */
stat = 0;
while ((stat==0) && (!status_eom_detected)) {
stat = do_qic_cmd(QCMD_RD_FM, TIM_F); /***** should use MTFSFM here???? ******/
}
if (tperror.exs & TP_NDT)
return 0;
#endif
return stat;
case MTERASE:
tpqputs("MTERASE -- ERASE TAPE !");
if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
tpqputs("Cartridge is write-protected.");
return -EACCES;
} else {
time_t t = jiffies;
/* give user a few seconds to pull out tape */
while (jiffies - t < 3*HZ)
schedule();
}
/* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */
if (mode_access==READ)
return -EACCES;
/* don't bother writing filemark */
status_eom_detected = status_eof_detected = NO;
return do_qic_cmd(QCMD_ERASE, TIM_R);
case MTRAS1:
#ifdef TP_HAVE_RAS1
tpqputs("MTRAS1: non-destructive self test");
stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R);
if (stat != 0) {
tpqputs("RAS1 failed");
return stat;
}
return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
#else
tpqputs("RAS1 not supported");
return -ENXIO;
#endif
case MTRAS2:
#ifdef TP_HAVE_RAS2
tpqputs("MTRAS2: destructive self test");
stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R);
if (stat != 0) {
tpqputs("RAS2 failed");
return stat;
}
return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
#else
tpqputs("RAS2 not supported");
return -ENXIO;
#endif
#ifdef TP_HAVE_SEEK
case MTSEEK:
tpqputs("MTSEEK seeking block");
if ((mode_access==WRITE) && status_bytes_wr)
return -EACCES;
/* NOTE: address (24 bits) is in seek_addr_buf[] */
return do_qic_cmd(QCMDV_SEEK_BLK, TIM_F);
#endif
default:
return -ENOTTY;
}
} /* do_ioctl_cmd */
/* dma_transfer(): This routine is called for every 512 bytes to be read
* from/written to the tape controller. Speed is important here!
* (There must be enough time left for the hd controller!)
* When other devices use DMA they must ensure they use un-interruptible
* double byte accesses to the DMA controller. Floppy.c is ok.
* Must have interrupts disabled when this function is invoked,
* otherwise, the double-byte transfers to the DMA controller will not
* be atomic. That could lead to nasty problems when they are interrupted
* by other DMA interrupt-routines.
*
* This routine merely does the least possible to keep
* the transfers going:
* - set the DMA count register for the next 512 bytes
* - adjust the DMA address and page registers
* - adjust the timeout
* - tell the tape controller to start transferring
* We assume the dma address and mode are, and remain, valid.
*/
static inline void dma_transfer(void)
{
#if TAPE_QIC02_IFC == WANGTEK
outb_p(WT_CTL_ONLINE, QIC_CTL_PORT); /* back to normal */
#elif TAPE_QIC02_IFC == ARCHIVE
outb_p(0, AR_RESET_DMA_PORT);
#endif
clear_dma_ff(TAPE_QIC02_DMA);
set_dma_mode(TAPE_QIC02_DMA, dma_mode);
set_dma_addr(TAPE_QIC02_DMA, buffaddr+dma_bytes_done); /* full address */
set_dma_count(TAPE_QIC02_DMA, TAPE_BLKSIZE);
/* start tape DMA controller */
#if TAPE_QIC02_IFC == WANGTEK
outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC_CTL_PORT); /* trigger DMA transfer */
#elif TAPE_QIC02_IFC == ARCHIVE
outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC_CTL_PORT); /* enable interrupts again */
outb_p(0, AR_START_DMA_PORT); /* start DMA transfer */
/* In dma_end() AR_RESET_DMA_PORT is written too. */
#endif
/* start computer DMA controller */
enable_dma(TAPE_QIC02_DMA);
/* block transfer should start now, jumping to the
* interrupt routine when done or an exception was detected.
*/
} /* dma_transfer */
/* start_dma() sets a DMA transfer up between the tape controller and
* the kernel tape_qic02_buf buffer.
* Normally bytes_todo==dma_bytes_done at the end of a DMA transfer. If not,
* a filemark was read, or an attempt to write beyond the End Of Tape
* was made. [Or some other bad thing happened.]
* Must do a sense() before returning error.
*/
static int start_dma(short mode, unsigned long bytes_todo)
/* assume 'bytes_todo'>0 */
{
int stat;
TPQPUTS("start_dma() enter");
TPQDEB({printk(TPQIC_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
dma_bytes_done = 0;
dma_bytes_todo = bytes_todo;
status_error = NO;
/* dma_mode!=0 indicates that the dma controller is in use */
dma_mode = (mode == WRITE)? DMA_MODE_WRITE : DMA_MODE_READ;
/* Only give READ/WRITE DATA command to tape drive if we haven't
* done that already. Otherwise the drive will rewind to the beginning
* of the current file on tape. Any QIC command given other than
* R/W FM will break the read/write transfer cycle.
* do_qic_cmd() will terminate doing_{read,write}
*/
if ((doing_read == NO) && (doing_write == NO)) {
/* First, we have to clear the status -- maybe remove TP_FIL???
*/
#if 0
/* Next dummy get status is to make sure CNI is valid,
since we're only just starting a read/write it doesn't
matter some exceptions are cleared by reading the status;
we're only interested in CNI and WRP. -Eddy */
get_status((char *) &tperror);
#else
/* TP_CNI should now be handled in open(). -Hennus */
#endif
stat = tp_sense(((mode == WRITE)? 0 : TP_WRP) | TP_BOM | TP_FIL);
if (stat != TE_OK)
return stat;
#if OBSOLETE
/************* not needed iff rd_status() would wait for ready!!!!!! **********/
if (wait_for_ready(TIM_S) != TE_OK) { /*** not sure this is needed ***/
tpqputs("wait_for_ready failed in start_dma");
return -EIO;
}
#endif
/* Tell the controller the data direction */
/* r/w, timeout medium, check exceptions, sets status_cmd_pending. */
stat = send_qic02_cmd((mode == WRITE)? QCMD_WRT_DATA : QCMD_RD_DATA, TIM_M, 0);
if (stat!=TE_OK) {
printk(TPQIC_NAME ": start_dma: init %s failed\n",
(mode == WRITE)? "write" : "read");
(void) tp_sense(0);
return stat;
}
/* Do this last, because sense() will clear the doing_{read,write}
* flags, causing trouble next time around.
*/
if (wait_for_ready(TIM_M) != TE_OK)
return -EIO;
switch (mode) {
case READ:
doing_read = YES;
break;
case WRITE:
doing_write = YES;
break;
default:
printk(TPQIC_NAME ": requested unknown mode %d\n", mode);
panic(TPQIC_NAME ": invalid mode in start_dma()");
}
} else if (is_exception()) {
/* This is for Archive drives, to handle reads with 0 bytes
* left for the last read request.
*
* ******** this also affects EOF/EOT handling! ************
*/
tpqputs("detected exception in start_dma() while transfer in progress");
status_error = YES;
return TE_END;
}
status_expect_int = YES;
/* This assumes tape is already positioned, but these
* semi-'intelligent' drives are unpredictable...
*/
TIMERON(TIM_M*2);
/* initiate first data block read from/write to the tape controller */
cli();
dma_transfer();
sti();
TPQPUTS("start_dma() end");
return TE_OK;
} /* start_dma */
/* This cleans up after the dma transfer has completed
* (or failed). If an exception occurred, a sense()
* must be done. If the exception was caused by a FM,
* sense() will set `status_eof_detected' and
* `status_eom_detected', as required.
*/
static void end_dma(unsigned long * bytes_done)
{
int stat = TE_OK;
TIMEROFF;
TPQPUTS("end_dma() enter");
disable_dma(TAPE_QIC02_DMA);
clear_dma_ff(TAPE_QIC02_DMA);
#if TAPE_QIC02_IFC == WANGTEK
outb_p(WT_CTL_ONLINE, QIC_CTL_PORT); /* back to normal */
#elif TAPE_QIC02_IFC == ARCHIVE
outb_p(0, AR_RESET_DMA_PORT);
#endif
stat = wait_for_ready(TIM_M);
if (status_error || (stat!=TE_OK)) {
tpqputs("DMA transfer exception");
stat = tp_sense((dma_mode==READ)? TP_WRP : 0);
/* no return here -- got to clean up first! */
}
/* take the tape controller offline */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -