📄 tpqic02.c
字号:
/* finish off DMA stuff */
dma_mode = 0;
/* Note: The drive is left on-line, ready for the next
* data transfer.
* If the next command to the drive does not continue
* the pending cycle, it must do 2 sense()s first.
*/
*bytes_done = dma_bytes_done;
status_expect_int = NO;
ioctl_status.mt_blkno += (dma_bytes_done / TAPE_BLKSIZE);
TPQPUTS("end_dma() exit");
/*** could return stat here ***/
} /* end_dma */
/*********** Below are the (public) OS-interface procedures ***********/
/* tape_qic02_times_out() is called when a DMA transfer doesn't complete
* quickly enough. Usually this means there is something seriously wrong
* with the hardware/software, but it could just be that the controller
* has decided to do a long rewind, just when I didn't expect it.
* Just try again.
*/
static void tape_qic02_times_out(void)
{
printk("time-out in %s driver\n", TPQIC_NAME);
if ((status_cmd_pending>0) || dma_mode) {
/* takes tooo long, shut it down */
status_dead = YES;
status_cmd_pending = 0;
status_timer_on = NO;
status_expect_int = NO;
status_error = YES;
if (dma_mode) {
dma_mode = 0; /* signal end to read/write routine */
wake_up(&tape_qic02_transfer);
}
}
} /* tape_qic02_times_out */
/*
* Interrupt handling:
*
* 1) Interrupt is generated iff at the end of
* a 512-DMA-block transfer.
* 2) EXCEPTION is not raised unless something
* is wrong or EOT/FM is detected.
* 3) FM EXCEPTION is set *after* the last byte has
* been transferred by DMA. By the time the interrupt
* is handled, the EXCEPTION may already be set.
*
* So,
* 1) On EXCEPTION, assume data has been transferred, so
* continue as usual, but set a flag to indicate the
* exception was detected.
* Do a sense status when the flag is found set.
* 2) Do not attempt to continue a transfer after an exception.
* [??? What about marginal blocks???????]
*/
/* tape_qic02_interrupt() is called when the tape controller completes
* a DMA transfer.
* We are not allowed to sleep here!
*
* Check if the transfer was successful, check if we need to transfer
* more. If the buffer contains enough data/is empty enough, signal the
* read/write() thread to copy to/from user space.
* When we are finished, set flags to indicate end, disable timer.
* NOTE: This *must* be fast!
*/
static void tape_qic02_interrupt(int unused)
{
int stat, r, i;
TIMEROFF;
if (status_expect_int) {
if (TP_DIAGS(current_tape_dev))
printk("@");
stat = inb(QIC_STAT_PORT); /* Knock, knock */
#if TAPE_QIC02_IFC == ARCHIVE /* "Who's there?" */
if (((stat & (AR_STAT_DMADONE)) == 0) &&
((stat & (QIC_STAT_EXCEPTION)) != 0)) {
TIMERCONT;
return; /* "Linux with IRQ sharing" */
}
#endif
if ((stat & QIC_STAT_EXCEPTION) == 0) { /* exception occurred */
/* Possible causes for an exception during a transfer:
* - during a write-cycle: end of tape (EW) hole detected.
* - during a read-cycle: filemark or EOD detected.
* - something went wrong
* So don't continue with the next block.
*/
tpqputs("isr: exception on tape controller");
printk(" status %02x\n", stat);
status_error = TE_EX;
dma_bytes_done += TAPE_BLKSIZE;
dma_mode = 0; /* wake up rw() */
status_expect_int = NO;
wake_up(&tape_qic02_transfer);
return;
}
/* return if tape controller not ready, or
* if dma channel hasn't finished last byte yet.
*/
r = 0;
/* Skip next ready check for Archive controller because
* it may be busy reading ahead. Weird. --hhb
*/
#if TAPE_QIC02_IFC != ARCHIVE /* I think this is a drive-dependency, not IFC -- hhb */
if (stat & QIC_STAT_READY) { /* not ready */
tpqputs("isr: ? Tape controller not ready");
r = 1;
}
#endif
if ( (i = get_dma_residue(TAPE_QIC02_DMA)) != 0 ) {
printk(TPQIC_NAME ": dma_residue == %x !!!\n", i);
r = 1; /* big trouble, but can't do much about it... */
}
if (r)
return;
/* finish DMA cycle */
/* no errors detected, continue */
dma_bytes_done += TAPE_BLKSIZE;
if (dma_bytes_done >= dma_bytes_todo) {
/* finished! Wakeup rw() */
dma_mode = 0;
status_expect_int = NO;
TPQPUTS("isr: dma_bytes_done");
wake_up(&tape_qic02_transfer);
} else {
/* start next transfer, account for track-switching time */
timer_table[TAPE_QIC02_TIMER].expires = jiffies + 6*HZ;
dma_transfer();
}
} else {
printk(TPQIC_NAME ": Unexpected interrupt, stat == %x\n",
inb(QIC_STAT_PORT));
}
} /* tape_qic02_interrupt */
static int tape_qic02_lseek(struct inode * inode, struct file * file, off_t offset, int origin)
{
return -EINVAL; /* not supported */
} /* tape_qic02_lseek */
/* read/write routines:
* This code copies between a kernel buffer and a user buffer. The
* actual data transfer is done using DMA and interrupts. Time-outs
* are also used.
*
* When a filemark is read, we return '0 bytes read' and continue with the
* next file after that.
* When EOM is read, we return '0 bytes read' twice.
* When the EOT marker is detected on writes, '0 bytes read' should be
* returned twice. If user program does a MTNOP after that, 2 additional
* blocks may be written. ------- FIXME: Implement this correctly *************************************************
*
* Only read/writes in multiples of 512 bytes are accepted.
* When no bytes are available, we sleep() until they are. The controller will
* generate an interrupt, and we (should) get a wake_up() call.
*
* Simple buffering is used. User program should ensure that a large enough
* buffer is used. Usually the drive does some buffering as well (something
* like 4k or so).
*
* Scott S. Bertilson suggested to continue filling the user buffer, rather
* than waste time on a context switch, when the kernel buffer fills up.
*/
/*
* Problem: tar(1) doesn't always read the entire file. Sometimes the entire file
* has been read, but the EOF token is never returned to tar(1), simply because
* tar(1) knows it has already read all of the data it needs. So we must use
* open/release to reset the `reported_read_eof' flag. If we don't, the next read
* request would return the EOF flag for the previous file.
*/
static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf, int count)
{
int error;
dev_t dev = inode->i_rdev;
unsigned short flags = filp->f_flags;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
int stat;
if (TP_DIAGS(current_tape_dev))
printk(TPQIC_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%x, flags=%x\n",
MINOR(dev), buf, count, filp->f_pos, flags);
if (count % TAPE_BLKSIZE) { /* Only allow mod 512 bytes at a time. */
tpqputs("Wrong block size");
return -EINVAL;
}
/* Just assume everything is ok. Controller will scream if not. */
if (status_bytes_wr) /* Once written, no more reads, 'till after WFM. */
return -EACCES;
/* Make sure buffer is safe to write into. */
error = verify_area(VERIFY_WRITE, buf, count);
if (error) {
printk(TPQIC_NAME ": read: verify_area(WRITE, %p, %x) failed\n", buf, count);
return error;
}
/* This is rather ugly because it has to implement a finite state
* machine in order to handle the EOF situations properly.
*/
while (count>=0) {
bytes_done = 0;
/* see how much fits in the kernel buffer */
bytes_todo = TPQBUF_SIZE;
if (bytes_todo>count)
bytes_todo = count;
/* Must ensure that user program sees exactly one EOF token (==0) */
if (return_read_eof==YES) {
printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%d\n", return_read_eof, reported_read_eof, total_bytes_done);
if (reported_read_eof==NO) {
/* have not yet returned EOF to user program */
if (total_bytes_done>0) {
return total_bytes_done; /* next time return EOF */
} else {
reported_read_eof = YES; /* move on next time */
return 0; /* return EOF */
}
} else {
/* Application program has already received EOF
* (above), now continue with next file on tape,
* if possible.
* When the FM is reached, EXCEPTION is set,
* causing a sense(). Subsequent read/writes will
* continue after the FM.
*/
/*********** ?????????? this should check for (EOD|NDT), not EOM, 'cause we can read past EW: ************/
if (status_eom_detected)
/* If EOM, nothing left to read, so keep returning EOFs.
*** should probably set some flag to avoid clearing
*** status_eom_detected through ioctls or something
*/
return 0;
else {
/* just eof, there may be more files ahead... */
return_read_eof = NO;
reported_read_eof = NO;
status_eof_detected = NO; /* reset this too */
/*fall through*/
}
}
}
/*****************************/
if (bytes_todo==0)
return total_bytes_done;
if (bytes_todo>0) {
/* start reading data */
if (is_exception()) /****************************************/
tpqputs("is_exception() before start_dma()!");
/******************************************************************
***** if start_dma() fails because the head is positioned 0 bytes
***** before the FM, (causing EXCEPTION to be set) return_read_eof should
***** be set to YES, and we should return total_bytes_done, rather than -ENXIO.
***** The app should recognize this as an EOF condition.
***************************************************************************/
stat = start_dma(READ, bytes_todo);
if (stat == TE_OK) {
/* Wait for transfer to complete, interrupt should wake us */
while (dma_mode != 0) {
sleep_on(&tape_qic02_transfer);
}
if (status_error)
return_read_eof = YES;
} else if (stat != TE_END) {
/* should do sense() on error here */
#if 0
return -ENXIO;
#else
printk("Trouble: stat==%02x\n", stat);
return_read_eof = YES;
/*************** check EOF/EOT handling!!!!!! **/
#endif
}
end_dma(&bytes_done);
if (bytes_done>bytes_todo) {
tpqputs("read: Oops, read more bytes than requested");
return -EIO;
}
/* copy buffer to user-space in one go */
if (bytes_done>0)
memcpy_tofs( (void *) buf, (void *) buffaddr, bytes_done);
#if 1
/* Checks Ton's patch below */
if ((return_read_eof == NO) && (status_eof_detected == YES)) {
printk(TPQIC_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
}
#endif
if ((bytes_todo != bytes_done) || (status_eof_detected == YES))
/* EOF or EOM detected. return EOF next time. */
return_read_eof = YES;
} /* else: ignore read request for 0 bytes */
if (bytes_done>0) {
status_bytes_rd = YES;
buf += bytes_done;
filp->f_pos += bytes_done;
total_bytes_done += bytes_done;
count -= bytes_done;
}
}
tpqputs("read request for <0 bytes");
return -EINVAL;
} /* tape_qic02_read */
/* The drive detects near-EOT by means of the holes in the tape.
* When the holes are detected, there is some space left. The drive
* reports this as a TP_EOM exception. After clearing the exception,
* the drive should accept two extra blocks.
*
* It seems there are some archiver programs that would like to use the
* extra space for writing a continuation marker. The driver should return
* end-of-file to the user program on writes, when the holes are detected.
* If the user-program wants to use the extra space, it should use the
* MTNOP ioctl() to get the generic status register and may then continue
* writing (max 1kB). ----------- doesn't work yet...............
*
* EOF behaviour on writes:
* If there is enough room, write all of the data.
* If there is insufficient room, write as much as will fit and
* return the amount written. If the requested amount differs from the
* written amount, the application program should recognize that as the
* end of file. Subsequent writes will return -ENOSPC.
* Unless the minor bits specify a rewind-on-close, the tape will not
* be rewound when it is full. The user-program should do that, if desired.
* If the driver were to do that automatically, a user-program could be
* confused about the EOT/BOT condition after re-opening the tape device.
*
* Multiple volume support: Tar closes the tape device before prompting for
* the next tape. The user may then insert a new tape and tar will open the
* tape device again. The driver will detect an exception status in (No Cartridge)
* and force a rewind. After that tar may continue writing.
*/
static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf, int count)
{
int error;
dev_t dev = inode->i_rdev;
unsigned short flags = filp->f_flags;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
if (TP_DIAGS(current_tape_dev))
printk(TPQIC_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%x, flags=%x\n",
MINOR(dev), buf, count, filp->f_pos, flags);
if (count % TAPE_BLKSIZE) { /* only allow mod 512 bytes at a time */
tpqputs("Wrong block size");
return -EINVAL;
}
if (mode_access==READ) {
tpqputs("Not in write mode");
return -EACCES;
}
/* open() does a sense() and we can assume the tape isn't changed
* between open() and release(), so the tperror.exs bits will still
* be valid.
*/
if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
tpqputs("Cartridge is write-protected.");
return -EACCES; /* don't even try when write protected */
}
/* Make sure buffer is safe to read from. */
error = verify_area(VERIFY_READ, buf, count);
if (error) {
printk(TPQIC_NAME ": write: verify_area(READ, %p, %x) failed\n", buf, count);
return error;
}
if (doing_read == YES)
terminate_read(0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -