📄 tpqic02.c
字号:
* 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); while (count>=0) { /* see how much fits in the kernel buffer */ bytes_done = 0; bytes_todo = TPQBUF_SIZE; if (bytes_todo>count) bytes_todo = count; if (return_write_eof == YES) { /* return_write_eof should be reset on reverse tape movements. */ if (reported_write_eof==NO) { if (bytes_todo>0) { tpqputs("partial write"); /* partial write signals EOF to user program */ } reported_write_eof = YES; return total_bytes_done; } else { return -ENOSPC; /* return error */ } } /* Quit when done. */ if (bytes_todo==0) return total_bytes_done; /* copy from user to DMA buffer and initiate transfer. */ if (bytes_todo>0) { memcpy_fromfs( (void *) buffaddr, (void *) buf, bytes_todo);/****************** similar problem with read() at FM could happen here at EOT. ******************//***** if at EOT, 0 bytes can be written. start_dma() will ***** fail and write() will return ENXIO error *****/ if (start_dma(WRITE, bytes_todo) != TE_OK) { tpqputs("write: start_dma() failed"); /* should do sense() on error here */ return -ENXIO; /*********** FIXTHIS **************/ } /* Wait for write to complete, interrupt should wake us. */ while ((status_error == 0) && (dma_mode != 0)) { sleep_on(&tape_qic02_transfer); } end_dma(&bytes_done); if (bytes_done>bytes_todo) { tpqputs("write: Oops, wrote more bytes than requested"); return -EIO; } /* If the dma-transfer was aborted because of an exception, * status_error will have been set in the interrupt handler. * Then end_dma() will do a sense(). * If the exception was EXC_EOM, the EW-hole was encountered * and two more blocks could be written. For the time being we'll * just consider this to be the EOT. * Otherwise, something Bad happened, such as the maximum number * of block-rewrites was exceeded. [e.g. A very bad spot on tape was * encountered. Normally short dropouts are compensated for by
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -