URL
https://opencores.org/ocsvn/or1k_old/or1k_old/trunk
Subversion Repositories or1k_old
[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [drivers/] [char/] [tpqic02.c] - Rev 199
Go to most recent revision | Compare with Previous | Blame | View Log
/* $Id: tpqic02.c,v 1.1.1.1 2001-09-10 07:44:17 simons Exp $ * * Driver for tape drive support for Linux-i386 1.1.58 * * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved. * Current e-mail address: hennus@sky.ow.org [This is a UUCP link.] * [If you are unable to reach me directly, try the TAPE mailing list * channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as * the first line in your message.] * * Distribution of this program in executable form is only allowed if * all of the corresponding source files are made available through the same * medium at no extra cost. * * I will not accept any responsibility for damage caused directly or * indirectly by this program, or code derived from this program. * * Use this code at your own risk. Don't blame me if it destroys your data! * Make sure you have a backup before you try this code. * * If you make changes to my code and redistribute it in source or binary * form you must make it clear to even casual users of your code that you * have modified my code, clearly point out what the changes exactly are * (preferably in the form of a context diff file), how to undo your changes, * where the original can be obtained, and that complaints/requests about the * modified code should be directed to you instead of me. * * This driver was partially inspired by the 'wt' driver in the 386BSD * source distribution, which carries the following copyright notice: * * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * You are not allowed to change this line nor the text above. * * $Log: not supported by cvs2svn $ * Revision 1.1.1.1 2001/07/02 17:58:25 simons * Initial revision * * Revision 0.4.1.5 1994/10/29 02:46:13 root * Minor cleanups. * * Revision 0.4.1.4 1994/07/21 02:15:45 root * ifdef'd DDI. Exception masks. * * Revision 0.4.1.3 1994/05/03 01:49:09 root * Initial attempt at Mountain support for the Mountain 7150. * Based on patches provided by Erik Jacobson. * * Revision 0.4.1.2 1994/03/18 21:16:50 root * Many driver messages can now be turned off (runtime selectable). * * Revision 0.4.1.1 1994/02/16 19:47:22 root * First stab at runtime debug-variable. * * Revision 0.4 1994/02/15 01:53:16 root * DYNCONF mark II. * Minor cleanups. * * Revision 0.3 1994/02/07 01:23:16 root * More improved DYNCONF. * Archive changes & some cleanups by Eddy Olk. * Removed status_open, more cleanups, misc other. * * Revision 0.2.1.25 1994/01/24 02:01:33 root * Changed tape_qic02 to QIC02_TAPE. * Changes to prepare for DYNCONF. * * Revision 0.2.1.24 1994/01/23 07:27:18 root * Attempt to remove compilation warnings, G++ bug, * Linus changed TAPE_QIC02 to QIC02_TAPE. * * Revision 0.2.1.23 1994/01/20 23:49:28 root * Changed some exception decoding stuff. * TP_HAVE_SEEK, TP_HAVE_DENS. byte_swap_w() on arg, not global. * Attempt to fix cartridge-changed-problem for 2150L. * Release irq and dma reservations if initial reset fails. * * Revision 0.2.1.22 1994/01/19 20:56:55 root * Speed measuring stuff moved from aperf.h to delay.h. * BogoMips (tm) introduced by Linus. * * Revision 0.2.1.21 1993/06/18 19:04:33 root * minor fixes for 0.99.10. * * Revision 0.2.1.20 1993/06/11 21:38:51 root * Added exception code for status 0x8000 (Cypher weirdness). * * Revision 0.2.1.19 1993/04/19 23:13:59 root * Cleanups. Changed to 0.99.8. * * Revision 0.2.1.18 1993/03/22 17:39:47 root * Moved to 0.99.7. Added Archive MTSEEK and MTTELL support. * * Revision 0.2.1.17 1993/03/08 18:51:59 root * Tried to `fix' write-once bug in previous release. * * Revision 0.2.1.16 1993/03/01 00:06:16 root * Use register_chrdev() for 0.99.6. * * Revision 0.2.1.15 1993/02/25 00:14:25 root * minor cleanups. * * Revision 0.2.1.14 1993/01/25 00:06:14 root * Kernel udelay. Eof fixups. * Removed report_ read/write dummies; have strace(1) now. * * Revision 0.2.1.13 1993/01/10 02:24:43 root * Rewrote wait_for_ready() to use newer schedule() features. * This improves performance for rewinds etc. * * Revision 0.2.1.12 1993/01/05 18:44:09 root * Changes for 0.99.1. Fixed `restartable reads'. * * Revision 0.2.1.11 1992/11/28 01:19:10 root * Changes to exception handling (significant). * Changed returned error codes. Hopefully they're correct now. * Changed declarations to please gcc-2.3.1. * Patch to deal with bogus interrupts for Archive cards. * * Revision 0.2.1.10 1992/10/28 00:50:44 root * underrun/error counter needed byte swapping. * * Revision 0.2.1.9 1992/10/15 17:06:01 root * Removed online() stuff. Changed EOF handling. * * Revision 0.2.1.8 1992/10/02 22:25:48 root * Removed `no_sleep' parameters (got usleep() now), * cleaned up some comments. * * Revision 0.2.1.7 1992/09/27 01:41:55 root * Changed write() to do entire user buffer in one go, rather than just * a kernel-buffer sized portion each time. * * Revision 0.2.1.6 1992/09/21 02:15:30 root * Introduced udelay() function for microsecond-delays. * Trying to use get_dma_residue rather than TC flags. * Patch to fill entire user buffer on reads before * returning. * * Revision 0.2.1.5 1992/09/19 02:31:28 root * Some changes based on patches by Eddy Olk to * support Archive SC402/SC499R controller cards. * * Revision 0.2.1.4 1992/09/07 01:37:37 root * Minor changes * * Revision 0.2.1.3 1992/08/13 00:11:02 root * Added some support for Archive SC402 and SC499 cards. * (Untested.) * * Revision 0.2.1.2 1992/08/10 02:02:36 root * Changed from linux/system.h macros to asm/dma.h inline functions. * * Revision 0.2.1.1 1992/08/08 01:12:39 root * cleaned up a bit. added stuff for selftesting. * preparing for asm/dma.h instead of linux/system.h * * Revision 0.2 1992/08/03 20:11:30 root * Changed to use new IRQ allocation. Padding now done at runtime, pads to * 512 bytes. Because of this the page regs must be re-programmed every * block! Added hooks for selftest commands. * Moved to linux-0.97. * * Revision 0.1.0.5 1992/06/22 22:20:30 root * moved to Linux 0.96b * * Revision 0.1.0.4 1992/06/18 02:00:04 root * Use minor bit-7 to enable/disable printing of extra debugging info * when do tape access. * Added semop stuff for DMA/IRQ allocation checking. Don't think this * is the right way to do it though. * * Revision 0.1.0.3 1992/06/01 01:57:34 root * changed DRQ to DMA. added TDEBUG ifdefs to reduce output. * * Revision 0.1.0.2 1992/05/31 14:02:38 root * changed SET_DMA_PAGE handling slightly. * * Revision 0.1.0.1 1992/05/27 12:12:03 root * Can now use multiple files on tape (sort of). * First release. * * Revision 0.1 1992/05/26 01:16:31 root * Initial version. Copyright H. H. Bergman 1992 * */ /* After the legalese, now the important bits: * * This is a driver for the Wangtek 5150 tape drive with * a QIC-02 controller for ISA-PC type computers. * Hopefully it will work with other QIC-02 tape drives as well. * * Make sure your setup matches the configuration parameters. * Also, be careful to avoid IO conflicts with other devices! */ /* #define TDEBUG */ #define REALLY_SLOW_IO /* it sure is ... */ #include <linux/sched.h> #include <linux/timer.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/errno.h> #include <linux/mtio.h> #include <linux/fcntl.h> #include <linux/delay.h> #include <linux/tpqic02.h> #include <linux/config.h> #include <linux/mm.h> #include <asm/dma.h> #include <asm/system.h> #include <asm/io.h> #include <asm/segment.h> /* We really shouldn't be using this define.. */ #define IOCCMD_MASK 0x0000ffff /* check existence of required configuration parameters */ #if !defined(QIC02_CMD_PORT) || \ !defined(QIC02_TAPE_IRQ) || \ !defined(QIC02_TAPE_DMA) #error qic02_tape configuration error #endif #define TPQIC02_NAME "tpqic02" /* Linux outb() commands have (value,port) as parameters. * One might expect (port,value) instead, so beware! */ #ifdef CONFIG_QIC02_DYNCONF /* This holds the dynamic configuration info for the interface * card+drive info if runtime configuration has been selected. */ struct mtconfiginfo qic02_tape_dynconf = { 0, }; /* user settable */ struct qic02_ccb qic02_tape_ccb = { 0, }; /* private stuff */ #else unsigned long qic02_tape_debug; # if ((QIC02_TAPE_IFC!=WANGTEK) && (QIC02_TAPE_IFC!=ARCHIVE) && (QIC02_TAPE_IFC!=MOUNTAIN)) # error No valid interface card specified # endif #endif static volatile int ctlbits = 0; /* control reg bits for tape interface */ static struct wait_queue *qic02_tape_transfer = NULL; /* sync rw with interrupts */ static volatile struct mtget ioctl_status; /* current generic status */ static volatile struct tpstatus tperror; /* last drive status */ static char rcs_revision[] = "$Revision: 1.1.1.1 $"; static char rcs_date[] = "$Date: 2001-09-10 07:44:17 $"; /* Flag bits for status and outstanding requests. * (Could all be put in one bit-field-struct.) * Some variables need `volatile' because they may be modified * by an interrupt. */ static volatile flag status_dead = YES; /* device is legally dead until proven alive */ static flag status_zombie = YES; /* it's `zombie' until irq/dma allocated */ static volatile flag status_bytes_wr = NO; /* write FM at close or not */ static volatile flag status_bytes_rd = NO; /* (rd|wr) used for rewinding */ static volatile unsigned long status_cmd_pending = 0; /* cmd in progress */ static volatile flag status_expect_int = NO; /* ready for interrupts */ static volatile flag status_timer_on = NO; /* using time-out */ static volatile int status_error = 0; /* int handler may detect error */ static volatile flag status_eof_detected = NO; /* end of file */ static volatile flag status_eom_detected = NO; /* end of recorded media */ static volatile flag status_eot_detected = NO; /* end of tape */ static volatile flag doing_read = NO; static volatile flag doing_write = NO; static volatile unsigned long dma_bytes_todo; static volatile unsigned long dma_bytes_done; static volatile unsigned dma_mode = 0; /* !=0 also means DMA in use */ static flag need_rewind = YES; static kdev_t current_tape_dev; static int extra_blocks_left = BLOCKS_BEYOND_EW; /* return_*_eof: * NO: not at EOF, * YES: tell app EOF was reached (return 0). * * return_*_eof==YES && reported_*_eof==NO ==> * return current buffer, next time(s) return EOF. * * return_*_eof==YES && reported_*_eof==YES ==> * at EOF and application knows it, so we can * move on to the next file. * */ static flag return_read_eof = NO; /* set to signal app EOF was reached */ static flag return_write_eof = NO; static flag reported_read_eof = NO; /* set when we've done that */ static flag reported_write_eof = NO; /* This is for doing `mt seek <blocknr>' */ static char seek_addr_buf[AR_SEEK_BUF_SIZE]; /* In write mode, we have to write a File Mark after the last block written, * when the tape device is closed. Tape repositioning and reading in write * mode is allowed as long as no actual writing has been done. After writing * the File Mark, repositioning and reading are allowed again. */ static int mode_access; /* access mode: READ or WRITE */ /* This is the actual kernel buffer where the interrupt routines read * from/write to. It is needed because the DMA channels 1 and 3 cannot * always access the user buffers. [The kernel buffer must reside in the * lower 16MBytes of system memory because of the DMA controller.] * The user must ensure that a large enough buffer is passed to the * kernel, in order to reduce tape repositioning. * * The buffer is 512 bytes larger than expected, because I want to align it * at 512 bytes, to prevent problems with 64k boundaries. */ static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE]; /* A really good compiler would be able to align this at 512 bytes... :-( */ static unsigned long buffaddr; /* aligned physical address of buffer */ /* This translates minor numbers to the corresponding recording format: */ static const char *format_names[] = { "not set", /* for dumb drives unable to handle format selection */ "11", /* extinct */ "24", "120", "150", "300", /* untested. */ "600" /* untested. */ }; /* `exception_list' is needed for exception status reporting. * Exceptions 1..14 are defined by QIC-02 rev F. * The drive status is matched sequentially to each entry, * ignoring irrelevant bits, until a match is found. If no * match is found, exception number 0 is used. (That should of * course never happen...) The original table was based on the * "Exception Status Summary" in QIC-02 rev F, but some changes * were required to make it work with real-world drives. * * Exception 2 (CNI) is changed to also cover status 0x00e0 (mask USL), * Exception 4 (EOM) is changed to also cover status 0x8288 (mask EOR), * Exception 11 (FIL) is changed to also cover status 0x0089 (mask EOM). * Exception 15 (EOR) is added for seek-to-end-of-data (catch EOR), * Exception 16 (BOM) is added for beginning-of-media (catch BOM). * * Had to swap EXC_NDRV and EXC_NCART to ensure that extended EXC_NCART * (because of the incorrect Wangtek status code) doesn't catch the * EXC_NDRV first. */ static struct exception_list_type { unsigned short mask, code; const char *msg; /* EXC_nr attribute should match with tpqic02.h */ } exception_list[] = { {0, 0, "Unknown exception status code", /* extra: 0 */}, {~(0), TP_ST0|TP_CNI|TP_USL|TP_WRP, "Drive not online" /* 1 */}, /* Drive presence goes before cartridge presence. */ {~(TP_WRP|TP_USL), TP_ST0|TP_CNI, /* My Wangtek 5150EQ sometimes reports a status code * of 0x00e0, which is not a valid exception code, but * I think it should be recognized as "NO CARTRIDGE". */ "Cartridge not in place" /* 2 */}, {(unsigned short) ~(TP_ST1|TP_BOM), (TP_ST0|TP_WRP), "Write protected cartridge" /* 3 */}, {(unsigned short) ~(TP_ST1|TP_EOR), (TP_ST0|TP_EOM), "End of media" /* 4 */}, {~TP_WRP, TP_ST0|TP_UDA| TP_ST1|TP_BOM, "Read or Write abort. Rewind tape." /* 5 */}, {~TP_WRP, TP_ST0|TP_UDA, "Read error. Bad block transferred." /* 6 */}, {~TP_WRP, TP_ST0|TP_UDA|TP_BNL, "Read error. Filler block transferred." /* 7 */}, {~TP_WRP, TP_ST0|TP_UDA|TP_BNL |TP_ST1|TP_NDT, "Read error. No data detected." /* 8 */}, {~TP_WRP, TP_ST0|TP_EOM|TP_UDA|TP_BNL |TP_ST1|TP_NDT, "Read error. No data detected. EOM." /* 9 */}, {~(TP_WRP|TP_MBD|TP_PAR|TP_EOR), TP_ST0|TP_UDA|TP_BNL |TP_ST1|TP_NDT|TP_BOM, "Read error. No data detected. BOM." /* 10 */}, {~(TP_WRP|TP_EOM), TP_ST0|TP_FIL, /* Status 0x0089 (EOM & FM) is viewed as an FM, * because it can only happen during a read. * EOM is checked separately for an FM condition. */ "File mark detected" /* 11 */}, {~(TP_ST0|TP_CNI|TP_USL|TP_WRP|TP_BOM), TP_ST1|TP_ILL, "Illegal command" /* 12 */}, {~(TP_ST0|TP_CNI|TP_USL|TP_WRP|TP_BOM), TP_ST1|TP_POR, "Reset occurred" /* 13 */}, {~TP_WRP, TP_ST0|TP_FIL|TP_MBD, /* NOTE: ST1 not set! */ "Marginal block detected" /* 14 */}, {~(TP_ST0|TP_WRP|TP_EOM|TP_UDA|TP_BNL|TP_FIL |TP_NDT), TP_ST1|TP_EOR, /********** Is the extra TP_NDT really needed Eddy? **********/ "End of recorded media" /* extra: 15 */}, /* 15 is returned when SEEKEOD completes successfully */ {~(TP_WRP|TP_ST0), TP_ST1|TP_BOM, "Beginning of media" /* extra: 16 */} }; #define NR_OF_EXC (sizeof(exception_list)/sizeof(struct exception_list_type)) static void tpqputs(unsigned long flags, const char *s) { if ((flags & TPQD_ALWAYS) || (flags & QIC02_TAPE_DEBUG)) printk(TPQIC02_NAME ": %s\n", s); } /* tpqputs */ /* Perform byte order swapping for a 16-bit word. * * [FIXME] This should probably be in include/asm/ * ([FIXME] i486 can do this faster) */ static inline void byte_swap_w(volatile unsigned short * w) { int t = *w; *w = (t>>8) | ((t & 0xff)<<8); } /* Init control register bits on interface card. * For Archive, interrupts must be enabled explicitly. * Wangtek interface card requires ONLINE to be set, Archive SC402/SC499R * cards keep it active all the time. */ static void ifc_init(void) { if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ { ctlbits = WT_CTL_ONLINE; /* online */ outb_p(ctlbits, QIC02_CTL_PORT); } else if (QIC02_TAPE_IFC == ARCHIVE) { ctlbits = 0; /* no interrupts yet */ outb_p(ctlbits, QIC02_CTL_PORT); outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */ } else /* MOUNTAIN */ { ctlbits = MTN_CTL_ONLINE; /* online, and logic enabled */ outb_p(ctlbits, QIC02_CTL_PORT); } } /* ifc_init */ static void report_exception(unsigned n) { if (n >= NR_OF_EXC) { tpqputs(TPQD_ALWAYS, "Oops -- report_exception"); n = 0; } if (TPQDBG(SENSE_TEXT) || n==0) printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg); } /* report_exception */ /* Try to map the drive-exception bits `s' to a predefined "exception number", * by comparing the significant exception bits for each entry in the * exception table (`exception_list[]'). * It is assumed that s!=0. */ static int decode_exception_nr(unsigned s) { int i; for (i=1; i<NR_OF_EXC; i++) { if ((s & exception_list[i].mask)==exception_list[i].code) return i; } printk(TPQIC02_NAME ": decode_exception_nr: exception(%x) not recognized\n", s); return 0; } /* decode_exception_nr */ #ifdef OBSOLETE /* There are exactly 14 possible exceptions, as defined in QIC-02 rev F. * Some are FATAL, some aren't. Currently all exceptions are treated as fatal. * Especially 6 and 14 should not abort the transfer. RSN... * Should probably let sense() figure out the exception number using the code * below, and just report the error based on the number here, returning a code * for FATAL/CONTINUABLE. */ static void report_error(int s) { short n = -1; if (s & TP_ST1) { if (s & TP_ILL) /* 12: Illegal command. FATAL */ n = 12; if (s & TP_POR) /* 13: Reset occurred. FATAL */ n = 13; } else if (s & TP_ST0) { if (s & TP_EOR) { /* extra: 15: End of Recorded Media. CONTINUABLE */ n = 15; /********** should set flag here **********/ } else if (s & TP_EOM) /* 4: End Of Media. CONTINUABLE */ n = 4; else if (s & TP_USL) /* 2: Drive not online. FATAL */ n = 2; else if (s & TP_CNI) { /* 1: Cartridge not in place. FATAL */ n = 1; need_rewind = YES; status_eof_detected = NO; status_eom_detected = NO; } else if (s & TP_UDA) { if (s & TP_BNL) { if (s & TP_NDT) { if (s & TP_BOM) /* 9: Read error. No data detected & EOM. CONTINUABLE */ n = 9; else if (s & TP_EOM) /* 10: Read error. No data detected & BOM. CONTINUABLE */ n = 10; else /* 8: Read error. No data detected. CONTINUABLE */ n = 8; } else { /* 7: Read error. Cannot recover block, filler substituted. CONTINUABLE */ tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]"); n = 7; } } else { if (s & TP_EOM) /* 5: Read or Write error. Rewind tape. FATAL */ n = 5; else { /* 6: Read error. Bad block transferred. CONTINUABLE */ /* block is bad, but transfer may continue. * This is why some people prefer not to * use compression on backups... */ tpqputs(TPQD_ALWAYS, "[CRC failed!]"); n = 6; } } } else if (s & TP_FIL) { if (s & TP_MBD) { /* 14: Marginal block detected. CONTINUABLE */ tpqputs(TPQD_ALWAYS, "[Marginal block]"); n = 14; } else /* 11: File mark detected. CONTINUABLE */ n = 11; } else if (s & TP_WRP) /* 3: Write protected cartridge. FATAL */ n = 3; } if (n >= 0) sensemsg(n); } /* report_error */ #endif /* Perform appropriate action for certain exceptions. * should return a value to indicate stop/continue (in case of bad blocks) */ static void handle_exception(int exnr, int exbits) { if (exnr==EXC_NCART) { /* Cartridge was changed. Redo sense(). * EXC_NCART should be handled in open(). * It is not permitted to remove the tape while * the tape driver has open files. */ need_rewind = YES; status_eof_detected = NO; status_eom_detected = NO; } else if (exnr==EXC_XFILLER) tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]"); else if (exnr==EXC_XBAD) tpqputs(TPQD_ALWAYS, "[CRC failed!]"); else if (exnr==EXC_MARGINAL) { /* A marginal block behaves much like a FM. * User may continue reading, if desired. */ tpqputs(TPQD_ALWAYS, "[Marginal block]"); doing_read = NO; } else if (exnr==EXC_FM) doing_read = NO; } /* handle_exception */ static inline int is_exception(void) { return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0; } /* is_exception */ /* Reset the tape drive and controller. * When reset fails, it marks the drive as dead and all * requests (except reset) are to be ignored (ENXIO). */ static int tape_reset(int verbose) { ifc_init(); /* reset interface card */ /* assert reset */ if (QIC02_TAPE_IFC == MOUNTAIN) outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); else /* WANGTEK, ARCHIVE */ outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT); /* Next, we need to wait >=25 usec. */ udelay(30); /* after reset, we will be at BOT (modulo an automatic rewind) */ status_eof_detected = NO; status_eom_detected = NO; status_cmd_pending = 0; need_rewind = YES; doing_read = doing_write = NO; ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0; /* de-assert reset */ if (QIC02_TAPE_IFC == MOUNTAIN) outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); else outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT); /* KLUDGE FOR G++ BUG */ { int stat = inb_p(QIC02_STAT_PORT); status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); } /* if successful, inb(STAT) returned RESETVAL */ if (status_dead == YES) printk(TPQIC02_NAME ": reset failed!\n"); else if (verbose) printk(TPQIC02_NAME ": reset successful\n"); return (status_dead == YES)? TE_DEAD : TE_OK; } /* tape_reset */ /* Notify tape drive of a new command. It only waits for the * command to be accepted, not for the actual command to complete. * * Before calling this routine, QIC02_CMD_PORT must have been loaded * with the command to be executed. * After this routine, the exception bit must be checked. * This routine is also used by rdstatus(), so in that case, any exception * must be ignored (`ignore_ex' flag). */ static int notify_cmd(char cmd, short ignore_ex) { int i; outb_p(cmd, QIC02_CMD_PORT); /* output the command */ /* wait 1 usec before asserting /REQUEST */ udelay(1); if ((!ignore_ex) && is_exception()) { tpqputs(TPQD_ALWAYS, "*** exception detected in notify_cmd"); /** force a reset here **/ if (tape_reset(1)==TE_DEAD) return TE_DEAD; if (is_exception()) { tpqputs(TPQD_ALWAYS, "exception persists after reset."); tpqputs(TPQD_ALWAYS, " ^ exception ignored."); } } outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* set request bit */ i = TAPE_NOTIFY_TIMEOUT; /* The specs say this takes about 500 usec, but there is no upper limit! * If the drive were busy retensioning or something like that, * it could be *much* longer! */ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) && (--i>0)) /*skip*/; /* wait for ready */ if (i==0) { tpqputs(TPQD_ALWAYS, "timed out waiting for ready in notify_cmd"); status_dead = YES; return TE_TIM; } outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* reset request bit */ i = TAPE_NOTIFY_TIMEOUT; /* according to the specs this one should never time-out */ while (((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0) && (--i>0)) /*skip*/; /* wait for not ready */ if (i==0) { tpqputs(TPQD_ALWAYS, "timed out waiting for !ready in notify_cmd"); status_dead = YES; return TE_TIM; } /* command accepted */ return TE_OK; } /* notify_cmd */ /* Wait for a command to complete, with timeout */ static int wait_for_ready(time_t timeout) { int stat; time_t spin_t; /* Wait for ready or exception, without driving the loadavg up too much. * In most cases, the tape drive already has READY asserted, * so optimize for that case. * * First, busy wait a few usec: */ spin_t = 50; while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (--spin_t>0)) /*SKIP*/; if ((stat & QIC02_STAT_READY) == 0) return TE_OK; /* covers 99.99% of all calls */ /* Then use schedule() a few times */ spin_t = 3; /* max 0.03 sec busy waiting */ if (spin_t > timeout) spin_t = timeout; timeout -= spin_t; spin_t += jiffies; while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t)) schedule(); /* don't waste all the CPU time */ if ((stat & QIC02_STAT_READY) == 0) return TE_OK; /* If we reach this point, we probably need to wait much longer, or * an exception occurred. Either case is not very time-critical. * Check the status port only a few times every second. * A interval of less than 0.10 sec will not be noticed by the user, * more than 0.40 sec may give noticeable delays. */ spin_t += timeout; TPQDEB({printk("wait_for_ready: additional timeout: %d\n", spin_t);}) /* not ready and no exception && timeout not expired yet */ while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t)) { /* be `nice` to other processes on long operations... */ current->timeout = jiffies + 3*HZ/10; /* nap 0.30 sec between checks, */ current->state = TASK_INTERRUPTIBLE; schedule(); /* but could be woken up earlier by signals... */ } /* don't use jiffies for this test because it may have changed by now */ if ((stat & QIC02_STAT_MASK) == QIC02_STAT_MASK) { tpqputs(TPQD_ALWAYS, "wait_for_ready() timed out"); return TE_TIM; } if ((stat & QIC02_STAT_EXCEPTION) == 0) { tpqputs(TPQD_ALWAYS, "exception detected after waiting_for_ready"); return TE_EX; } else { return TE_OK; } } /* wait_for_ready */ /* Send some data to the drive */ static int send_qic02_data(char sb[], unsigned size, int ignore_ex) { int i, stat; for (i=0; i<size; i++) { stat = wait_for_ready(TIM_S); if (stat != TE_OK) return stat; stat = notify_cmd(sb[i], ignore_ex); if (stat != TE_OK) return stat; } return TE_OK; } /* send_qic02_data */ /* Send a QIC-02 command (`cmd') to the tape drive, with * a time-out (`timeout'). * This one is also used by tp_sense(), so we must have * a flag to disable exception checking (`ignore_ex'). * * On entry, the controller is supposed to be READY. */ static int send_qic02_cmd(int cmd, time_t timeout, int ignore_ex) { int stat; stat = inb_p(QIC02_STAT_PORT); if ((stat & QIC02_STAT_EXCEPTION) == 0) { /* if exception */ tpqputs(TPQD_ALWAYS, "send_qic02_cmd: Exception!"); return TE_EX; } if (stat & QIC02_STAT_READY) { /* if not ready */ tpqputs(TPQD_ALWAYS, "send_qic02_cmd: not Ready!"); return TE_ERR; } /* assert(ready & !exception) */ /* Remember current command for later re-use with dma transfers. * (For reading/writing multiple blocks.) */ status_cmd_pending = cmd; stat = notify_cmd(cmd, ignore_ex); /* tell drive new command was loaded, */ /* inherit exception check. */ if (TP_HAVE_SEEK && (cmd == AR_QCMDV_SEEK_BLK)) { /* This one needs to send 3 more bytes, MSB first */ stat = send_qic02_data(seek_addr_buf, sizeof(seek_addr_buf), ignore_ex); } if (stat != TE_OK) { tpqputs(TPQD_ALWAYS, "send_qic02_cmd failed"); } return stat; } /* send_qic02_cmd */ /* Get drive status. Assume drive is ready or has exception set. * (or will be in <1000 usec.) * Extra parameters added because of 'Read Extended Status 3' command. */ static int rdstatus(char *stp, unsigned size, char qcmd) { int s, n; char *q = stp; /* Try to busy-wait a few (700) usec, after that de-schedule. * * The problem is, if we don't de-schedule, performance will * drop to zero when the drive is not responding and if we * de-schedule immediately, we waste a lot of time because a * task switch is much longer than we usually have to wait here. */ n = 1000; /* 500 is not enough on a 486/33 */ while ((n>0) && ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK)) n--; /* wait for ready or exception or timeout */ if (n==0) { /* n (above) should be chosen such that on your machine * you rarely ever see the message below, and it should * be small enough to give reasonable response time.] */ tpqputs(TPQD_ALWAYS, "waiting looong in rdstatus() -- drive dead?"); while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) schedule(); tpqputs(TPQD_ALWAYS, "finished waiting in rdstatus()"); } (void) notify_cmd(qcmd, 1); /* send read status command */ /* ignore return code -- should always be ok, STAT may contain * exception flag from previous exception which we are trying to clear. */ if (TP_DIAGS(current_tape_dev)) printk(TPQIC02_NAME ": reading status bytes: "); for (q=stp; q<stp+size; q++) { do s = inb_p(QIC02_STAT_PORT); while ((s & QIC02_STAT_MASK) == QIC02_STAT_MASK); /* wait for ready or exception */ if ((s & QIC02_STAT_EXCEPTION) == 0) { /* if exception */ tpqputs(TPQD_ALWAYS, "rdstatus: exception error"); ioctl_status.mt_erreg = 0; /* dunno... */ return TE_NS; /* error, shouldn't happen... */ } *q = inb_p(QIC02_DATA_PORT); /* read status byte */ if (TP_DIAGS(current_tape_dev)) printk("[%1d]=0x%x ", q-stp, (unsigned) (*q) & 0xff); outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* set request */ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0); /* wait for not ready */ udelay(22); /* delay >20 usec */ outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* un-set request */ } /* Specs say we should wait for READY here. * My drive doesn't seem to need it here yet, but others do? */ while (inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) /*skip*/; /* wait for ready */ if (TP_DIAGS(current_tape_dev)) printk("\n"); return TE_OK; } /* rdstatus */ /* Get standard status (6 bytes). * The `.dec' and `.urc' fields are in MSB-first byte-order, * so they have to be swapped first. */ static int get_status(volatile struct tpstatus *stp) { int stat = rdstatus((char *) stp, TPSTATSIZE, QCMD_RD_STAT); #if defined(i386) || defined(i486) byte_swap_w(&(stp->dec)); byte_swap_w(&(stp->urc)); #else /* should probably swap status bytes #definition */ #endif return stat; } /* get_status */ #if 0 /* This fails for my Wangtek drive */ /* get "Extended Status Register 3" (64 bytes) * * If the meaning of the returned bytes were known, the MT_TYPE * identifier could be used to decode them, since they are * "vendor unique". :-( */ static int get_ext_status3(void) { char vus[64]; /* vendor unique status */ int stat, i; tpqputs(TPQD_ALWAYS, "Attempting to read Extended Status 3..."); stat = rdstatus(vus, sizeof(vus), QCMD_RD_STAT_X3); if (stat != TE_OK) return stat; tpqputs(TPQD_ALWAYS, "Returned status bytes:"); for (i=0; i<sizeof(vus); i++) { if ( i % 8 == 0 ) printk("\n" TPQIC02_NAME ": %2d:"); printk(" %2x", vus[i] & 0xff); } printk("\n"); return TE_OK; } /* get_ext_status3 */ #endif /* Read drive status and set generic status too. * NOTE: Once we do a tp_sense(), read/write transfers are killed. */ static int tp_sense(int ignore) { unsigned err = 0, exnr = 0, gs = 0; static void finish_rw(int cmd); if (TPQDBG(SENSE_TEXT)) printk(TPQIC02_NAME ": tp_sense(ignore=0x%x) enter\n", ignore); /* sense() is not allowed during a read or write cycle */ if (doing_write == YES) tpqputs(TPQD_ALWAYS, "Warning: File Mark inserted because of sense() request"); /* The extra test is to avoid calling finish_rw during booting */ if ((doing_read!=NO) || (doing_write!=NO)) finish_rw(QCMD_RD_STAT); if (get_status(&tperror) != TE_OK) { tpqputs(TPQD_ALWAYS, "tp_sense: could not read tape drive status"); return TE_ERR; } err = tperror.exs; /* get exception status bits */ if (err & (TP_ST0|TP_ST1)) printk(TPQIC02_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n", tperror.exs, tperror.dec, tperror.urc); else if ((tperror.dec!=0) || (tperror.urc!=0) || TPQDBG(SENSE_CNTS)) printk(TPQIC02_NAME ": tp_sense: no hard errors, soft error count: %d, underruns: %d\n", tperror.dec, tperror.urc); /* Set generic status. HP-UX defines these, but some extra would * be useful. Problem is to remain compatible. [Do we want to be * compatible??] */ if (err & TP_ST0) { if (err & TP_CNI) /* no cartridge */ gs |= GMT_DR_OPEN(-1); if (status_dead == NO) gs |= GMT_ONLINE(-1); /* always online */ if (err & TP_USL) /* not online */ gs &= ~GMT_ONLINE(-1); if (err & TP_WRP) gs |= GMT_WR_PROT(-1); if (err & TP_EOM) { /* end of media */ gs |= GMT_EOT(-1); /* not sure this is correct for writes */ status_eom_detected = YES; /* I don't know whether drive always reports EOF at or before EOM. */ status_eof_detected = YES; } /** if (err & TP_UDA) "Unrecoverable data error" **/ /** if (err & TP_BNL) "Bad block not located" **/ if (err & TP_FIL) { gs |= GMT_EOF(-1); status_eof_detected = YES; } } if (err & TP_ST1) { /** if (err & TP_ILL) "Illegal command" **/ /** if (err & TP_NDT) "No data detected" **/ /** if (err & TP_MBD) "Marginal block detected" **/ if (err & TP_BOM) gs |= GMT_BOT(-1); /* beginning of tape */ } ioctl_status.mt_gstat = gs; ioctl_status.mt_dsreg = tperror.exs; /* "drive status" */ ioctl_status.mt_erreg = tperror.dec; /* "sense key error" */ if (err & (TP_ST0|TP_ST1)) { /* My Wangtek occasionally reports `status' 1212 which should be ignored. */ exnr = decode_exception_nr(err); handle_exception(exnr, err); /* update driver state wrt drive status */ report_exception(exnr); } err &= ~ignore; /* mask unwanted errors -- not the correct way, use exception nrs?? */ if (((err & TP_ST0) && (err & REPORT_ERR0)) || ((err & TP_ST1) && (err & REPORT_ERR1))) return TE_ERR; return TE_OK; } /* tp_sense */ /* Wait for a wind or rewind operation to finish or * to time-out. (May take very long). */ static int wait_for_rewind(time_t timeout) { int stat; stat = inb(QIC02_STAT_PORT) & QIC02_STAT_MASK; if (TPQDBG(REWIND)) printk(TPQIC02_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat); stat = wait_for_ready(timeout); if (stat != TE_OK) { tpqputs(TPQD_ALWAYS, "(re-) winding failed\n"); } return stat; } /* wait_for_rewind */ /* Perform a full QIC02 command, and wait for completion, * check status when done. Complain about exceptions. * * This function should return an OS error code when * something goes wrong, 0 otherwise. */ static int ll_do_qic_cmd(int cmd, time_t timeout) { int stat; if (status_dead == YES) { tpqputs(TPQD_ALWAYS, "Drive is dead. Do a `mt reset`."); return -ENXIO; /* User should do an MTRESET. */ } stat = wait_for_ready(timeout); /* wait for ready or exception */ if (stat == TE_EX) { if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) return -EIO; /* else nothing to worry about, I hope */ stat = TE_OK; } if (stat != TE_OK) { printk(TPQIC02_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, (long) timeout); return -EIO; } #if OBSOLETE /* wait for ready since it may not be active immediately after reading status */ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) != 0); #endif stat = send_qic02_cmd(cmd, timeout, 0); /* (checks for exceptions) */ if (cmd==QCMD_RD_FM) { status_eof_detected = NO; ioctl_status.mt_fileno++; /* Should update block count as well, but can't. * Can do a `read address' for some drives, when MTNOP is done. */ } else if (cmd==QCMD_WRT_FM) { status_eof_detected = NO; ioctl_status.mt_fileno++; } else if ((cmd==QCMD_REWIND) || (cmd==QCMD_ERASE) || (cmd==QCMD_RETEN)) { status_eof_detected = NO; status_eom_detected = NO; status_eot_detected = NO; need_rewind = NO; ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0; extra_blocks_left = BLOCKS_BEYOND_EW; return_write_eof = NO; return_read_eof = NO; reported_read_eof = NO; reported_write_eof = NO; } /* sense() will set eof/eom as required */ if (stat==TE_EX) { if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) { printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, (long) timeout); status_dead = YES; return -ENXIO; /* if rdstatus fails too, we're in trouble */ } } else if (stat!=TE_OK) { printk(TPQIC02_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat); return -EIO; /*** -EIO is probably not always appropriate */ } if (timeout == TIM_R) stat = wait_for_rewind(timeout); else stat = wait_for_ready(timeout); if (stat==TE_EX) { if (tp_sense((cmd==QCMD_SEEK_EOD ? /*****************************/ TP_EOR|TP_NDT|TP_UDA|TP_BNL|TP_WRP|TP_BOM|TP_EOM|TP_FIL : TP_WRP|TP_BOM|TP_EOM|TP_FIL))!=TE_OK) { printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, (long) timeout); if (cmd!=QCMD_RD_FM) status_dead = YES; return -ENXIO; /* if rdstatus fails too, we're in trouble */ } } else if (stat!=TE_OK) { printk(TPQIC02_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat); return -EIO; } return 0; } /* ll_do_qic_cmd */ /* * Problem: What to do when the user cancels a read/write operation * in-progress? * * "Deactivating ONLINE during a READ also causes the" * "tape to be rewound to BOT." Ditto for WRITEs, except * a FM is written first. "The host may alternatively terminate * the READ/WRITE command by issuing a RFM/WFM command." * * For READs: * Neither option will leave the tape positioned where it was. * Another (better?) solution is to terminate the READ by two * subsequent sense() operations, the first to stop the current * READ cycle, the second to clear the `Illegal command' exception, * because the QIC-02 specs didn't anticipate this. This is * delayed until actually needed, so a tar listing can be aborted * by the user and continued later. * If anybody has a better solution, let me know! [Also, let me * know if your drive (mine is a Wangtek5150EQ) does not accept * this sequence for canceling the read-cycle.] * * For WRITEs it's simple: Just do a WRITE_FM, leaving the tape * positioned after the FM. */ static void terminate_read(int cmd) { if (doing_read == YES) { doing_read = NO; if (cmd != QCMD_RD_FM) { /* if the command is a RFM, there is no need to do this * because a RFM will legally terminate the read-cycle. */ tpqputs(TPQD_ALWAYS, "terminating pending read-cycle"); /* I'm not too sure about this part -- hhb */ if (QIC02_TAPE_IFC == MOUNTAIN) { /* Mountain reference says can terminate by de-asserting online */ ctlbits &= ~MTN_QIC02_CTL_ONLINE; } if (tp_sense(TP_FIL|TP_EOM|TP_WRP) != TE_OK) { tpqputs(TPQD_ALWAYS, "finish_rw[read1]: ignore the 2 lines above"); if (is_exception()) { if (tp_sense(TP_ILL|TP_FIL|TP_EOM|TP_WRP) != TE_OK) tpqputs(TPQD_ALWAYS, "finish_rw[read2]: read cycle error"); } } } } } /* terminate_read */ static void terminate_write(int cmd) { int stat; if (doing_write == YES) { doing_write = NO; /* Finish writing by appending a FileMark at the end. */ if (cmd != QCMD_WRT_FM) { /* finish off write cycle */ stat = ll_do_qic_cmd(QCMD_WRT_FM, TIM_M); if (stat != TE_OK) tpqputs(TPQD_ALWAYS, "Couldn't finish write cycle properly"); (void) tp_sense(0); } /* If there is an EOF token waiting to be returned to * the (writing) application, discard it now. * We could be at EOT, so don't reset return_write_eof. */ reported_write_eof=YES; } } /* terminate_write */ /* terminate read or write cycle because of command `cmd' */ static void finish_rw(int cmd) { if (wait_for_ready(TIM_S) != TE_OK) { tpqputs(TPQD_ALWAYS, "error: drive not ready in finish_rw() !"); return; } terminate_read(cmd); terminate_write(cmd); } /* finish_rw */ /* Perform a QIC command through ll_do_qic_cmd(). * If necessary, rewind the tape first. * Return an OS error code if something goes wrong, 0 if all is well. */ static int do_qic_cmd(int cmd, time_t timeout) { int stat; finish_rw(cmd); if (need_rewind) { tpqputs(TPQD_REWIND, "Rewinding tape..."); stat = ll_do_qic_cmd(QCMD_REWIND, TIM_R); if (stat != 0) { printk(TPQIC02_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat); return stat; } need_rewind = NO; if (cmd==QCMD_REWIND) /* don't wind beyond BOT ;-) */ return 0; } return ll_do_qic_cmd(cmd, timeout); } /* do_qic_cmd */ /* Not all ioctls are supported for all drives. Some rely on * optional QIC-02 commands. Check tpqic02.h for configuration. * Some of these commands may require ONLINE to be active. */ static int do_ioctl_cmd(int cmd) { int stat; /* It is not permitted to read or wind the tape after bytes have * been written. It is not permitted to write the tape while in * read mode. * We try to be kind and allow reading again after writing a FM... */ switch (cmd) { case MTRESET: /* reset verbose */ return (tape_reset(1)==TE_OK)? 0 : -EIO; case MTFSF: tpqputs(TPQD_IOCTLS, "MTFSF forward searching filemark"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; return do_qic_cmd(QCMD_RD_FM, TIM_F); case MTBSF: if (TP_HAVE_BSF) { tpqputs(TPQD_IOCTLS, "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 { stat = -ENXIO; } status_eom_detected = status_eof_detected = NO; return stat; case MTFSR: if (TP_HAVE_FSR) { /* This is an optional QIC-02 command */ tpqputs(TPQD_IOCTLS, "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(TPQD_IOCTLS, "MTFSR not supported"); stat = -ENXIO; } return stat; case MTBSR: if (TP_HAVE_BSR) { /* This is an optional QIC-02 command */ /* we need this for appending files with GNU tar!! */ tpqputs(TPQD_IOCTLS, "MTFSR backward space record"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F); } else { tpqputs(TPQD_IOCTLS, "MTBSR not supported"); stat = -ENXIO; } status_eom_detected = status_eof_detected = NO; return stat; case MTWEOF: tpqputs(TPQD_IOCTLS, "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(TPQD_IOCTLS, "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(TPQD_IOCTLS, "MTOFFL rewinding & going 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(TPQD_IOCTLS, "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(TPQD_IOCTLS, "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(TPQD_IOCTLS, "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(TPQD_IOCTLS, "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(TPQD_IOCTLS, "MTEOM search for End Of recorded Media"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; if (TP_HAVE_EOD) { /* Use faster seeking when possible. * This requires the absence of data beyond the EOM. * 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; 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; } return stat; case MTERASE: tpqputs(TPQD_IOCTLS, "MTERASE -- ERASE TAPE !"); if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) { tpqputs(TPQD_ALWAYS, "Cartridge is write-protected."); return -EACCES; } else { time_t t = jiffies; /* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */ if (mode_access==READ) return -EACCES; /* give user a few seconds to pull out tape */ while (jiffies - t < 4*HZ) schedule(); } /* don't bother writing filemark first */ status_eom_detected = status_eof_detected = NO; return do_qic_cmd(QCMD_ERASE, TIM_R); case MTRAS1: if (TP_HAVE_RAS1) { tpqputs(TPQD_IOCTLS, "MTRAS1: non-destructive self test"); stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R); if (stat != 0) { tpqputs(TPQD_ALWAYS, "RAS1 failed"); return stat; } return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */ } tpqputs(TPQD_IOCTLS, "RAS1 not supported"); return -ENXIO; case MTRAS2: if (TP_HAVE_RAS2) { tpqputs(TPQD_IOCTLS, "MTRAS2: destructive self test"); stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R); if (stat != 0) { tpqputs(TPQD_ALWAYS, "RAS2 failed"); return stat; } return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */ } tpqputs(TPQD_IOCTLS, "RAS2 not supported"); return -ENXIO; case MTSEEK: if (TP_HAVE_SEEK && (QIC02_TAPE_IFC==ARCHIVE)) { tpqputs(TPQD_IOCTLS, "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(AR_QCMDV_SEEK_BLK, TIM_F); } else return -ENOTTY; 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 (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */ outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */ else if (QIC02_TAPE_IFC == ARCHIVE) outb_p(0, AR_RESET_DMA_PORT); else /* QIC02_TAPE_IFC == MOUNTAIN */ outb_p(ctlbits, QIC02_CTL_PORT); clear_dma_ff(QIC02_TAPE_DMA); set_dma_mode(QIC02_TAPE_DMA, dma_mode); set_dma_addr(QIC02_TAPE_DMA, buffaddr+dma_bytes_done); /* full address */ set_dma_count(QIC02_TAPE_DMA, TAPE_BLKSIZE); /* start tape DMA controller */ if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */ outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC02_CTL_PORT); /* trigger DMA transfer */ else if (QIC02_TAPE_IFC == ARCHIVE) { outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC02_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. */ } else /* QIC02_TAPE_IFC == MOUNTAIN */ { inb(MTN_R_DESELECT_DMA_PORT); outb_p(ctlbits | (MTN_CTL_EXC_IEN | MTN_CTL_DNIEN), QIC02_CTL_PORT); outb_p(0, MTN_W_SELECT_DMA_PORT); /* start DMA transfer */ if (dma_mode == DMA_MODE_WRITE) outb_p(0, MTN_W_DMA_WRITE_PORT); /* start DMA transfer */ } /* start computer DMA controller */ enable_dma(QIC02_TAPE_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 qic02_tape_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(TPQD_DEBUG, "start_dma() enter"); TPQDEB({printk(TPQIC02_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(&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(TPQD_ALWAYS, "wait_for_ready failed in start_dma"); return -EIO; } #endif if (QIC02_TAPE_IFC == MOUNTAIN) { /* Set control bits to select ONLINE during command */ ctlbits |= MTN_QIC02_CTL_ONLINE; } /* 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(TPQIC02_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(TPQIC02_NAME ": requested unknown mode %d\n", mode); panic(TPQIC02_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(TPQD_ALWAYS, "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(QIC02_TAPE_DMA); clear_dma_ff(QIC02_TAPE_DMA); if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */ outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */ else if (QIC02_TAPE_IFC == ARCHIVE) outb_p(0, AR_RESET_DMA_PORT); else /* QIC02_TAPE_IFC == MOUNTAIN */ { /* Clear control bits, de-select ONLINE during tp_sense */ ctlbits &= ~MTN_QIC02_CTL_ONLINE; } stat = wait_for_ready(TIM_M); if (status_error || (stat!=TE_OK)) { tpqputs(TPQD_DMAX, "DMA transfer exception"); stat = tp_sense((dma_mode==READ)? TP_WRP : 0); /* no return here -- got to clean up first! */ } else /* if (QIC02_TAPE_IFC == MOUNTAIN) */ { outb_p(ctlbits, QIC02_CTL_PORT); } if (QIC02_TAPE_IFC == MOUNTAIN) inb(MTN_R_DESELECT_DMA_PORT); /* take the tape controller offline */ /* 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 ***********/ /* qic02_tape_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 qic02_tape_times_out(void) { printk("time-out in %s driver\n", TPQIC02_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(&qic02_tape_transfer); } } } /* qic02_tape_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???????] */ /* qic02_tape_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 qic02_tape_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int stat, r, i; TIMEROFF; if (status_expect_int) { #ifdef WANT_EXTRA_FULL_DEBUGGING if (TP_DIAGS(current_tape_dev)) printk("@"); #endif stat = inb(QIC02_STAT_PORT); /* Knock, knock */ if (QIC02_TAPE_IFC == ARCHIVE) { /* "Who's there?" */ if (((stat & (AR_STAT_DMADONE)) == 0) && ((stat & (QIC02_STAT_EXCEPTION)) != 0)) { TIMERCONT; return; /* "Linux with IRQ sharing" */ } } if ((stat & QIC02_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(TPQD_ALWAYS, "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(&qic02_tape_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 (QIC02_TAPE_IFC == WANGTEK) /* I think this is a drive-dependency, not IFC -- hhb */ if (stat & QIC02_STAT_READY) { /* not ready */ tpqputs(TPQD_ALWAYS, "isr: ? Tape controller not ready"); r = 1; } if ( (i = get_dma_residue(QIC02_TAPE_DMA)) != 0 ) { printk(TPQIC02_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(&qic02_tape_transfer); } else { /* start next transfer, account for track-switching time */ timer_table[QIC02_TAPE_TIMER].expires = jiffies + 6*HZ; dma_transfer(); } } else { printk(TPQIC02_NAME ": Unexpected interrupt, stat == %x\n", inb(QIC02_STAT_PORT)); } } /* qic02_tape_interrupt */ static int qic02_tape_lseek(struct inode * inode, struct file * file, off_t offset, int origin) { return -EINVAL; /* not supported */ } /* qic02_tape_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 qic02_tape_read(struct inode * inode, struct file * filp, char * buf, int count) { int error; kdev_t dev = inode->i_rdev; unsigned short flags = filp->f_flags; unsigned long bytes_todo, bytes_done, total_bytes_done = 0; int stat; if (status_zombie==YES) { tpqputs(TPQD_ALWAYS, "configs not set"); return -ENXIO; } if (TP_DIAGS(current_tape_dev)) /* can't print a ``long long'' (for filp->f_pos), so chop it */ printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n", MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags); if (count % TAPE_BLKSIZE) { /* Only allow mod 512 bytes at a time. */ tpqputs(TPQD_BLKSZ, "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) 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) { if (TPQDBG(DEBUG)) printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\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(TPQD_DMAX, "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(&qic02_tape_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(TPQD_ALWAYS, "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(TPQIC02_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(TPQD_ALWAYS, "read request for <0 bytes"); return -EINVAL; } /* qic02_tape_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 qic02_tape_write(struct inode * inode, struct file * filp, const char * buf, int count) { int error; kdev_t dev = inode->i_rdev; unsigned short flags = filp->f_flags; unsigned long bytes_todo, bytes_done, total_bytes_done = 0; if (status_zombie==YES) { tpqputs(TPQD_ALWAYS, "configs not set"); return -ENXIO; } if (TP_DIAGS(current_tape_dev)) /* can't print a ``long long'' (for filp->f_pos), so chop it */ printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n", MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags); if (count % TAPE_BLKSIZE) { /* only allow mod 512 bytes at a time */ tpqputs(TPQD_BLKSZ, "Wrong block size"); return -EINVAL; } if (mode_access==READ) { tpqputs(TPQD_ALWAYS, "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(TPQD_ALWAYS, "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) 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(TPQD_ALWAYS, "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, (const 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(TPQD_ALWAYS, "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(&qic02_tape_transfer); } end_dma(&bytes_done); if (bytes_done>bytes_todo) { tpqputs(TPQD_ALWAYS, "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 * rewriting the block in error, up to 16 times. I'm not sure * QIC-24 drives can do this.] */ if (status_error) { if (status_eom_detected == YES) { tpqputs(TPQD_ALWAYS, "write: EW detected"); return_write_eof = YES; } else { /* probably EXC_RWA */ tpqputs(TPQD_ALWAYS, "write: dma: error in writing"); return -EIO; } } if (bytes_todo != bytes_done) /* EOF or EOM detected. return EOT next time. */ return_write_eof = YES; } /* else: ignore write request for 0 bytes. */ if (bytes_done>0) { status_bytes_wr = YES; buf += bytes_done; filp->f_pos += bytes_done; total_bytes_done += bytes_done; count -= bytes_done; } } tpqputs(TPQD_ALWAYS, "write request for <0 bytes"); if (TPQDBG(DEBUG)) printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %x\n", status_bytes_wr, buf, total_bytes_done, count); return -EINVAL; } /* qic02_tape_write */ /* qic02_tape_open() * We allow the device to be opened, even if it is marked 'dead' because * we want to be able to reset the tape device without rebooting. * Only one open tape file at a time, except when minor=255. * Minor 255 is only allowed for resetting and always returns <0. * * The density command is only allowed when TP_BOM is set. Thus, remember * the most recently used minor bits. When they are different from the * remembered values, rewind the tape and set the required density. * Don't rewind if the minor bits specify density 0. */ static int qic02_tape_open(struct inode * inode, struct file * filp) { kdev_t dev = inode->i_rdev; unsigned short flags = filp->f_flags; unsigned short dens = 0; int s; if (TP_DIAGS(dev)) { printk("qic02_tape_open: dev=%s, flags=%x ", kdevname(dev), flags); } if (MINOR(dev)==255) /* special case for resetting */ if (suser()) return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO; else return -EPERM; if (status_dead==YES) /* Allow `mt reset' ioctl() even when already open()ed. */ return 0; /* Only one at a time from here on... */ if (filp->f_count>1) { /* filp->f_count==1 for the first open() */ return -EBUSY; } if (status_zombie==YES) /* no irq/dma/port stuff allocated yet, no reset done * yet, so return until MTSETCONFIG has been done. */ return 0; status_bytes_rd = NO; status_bytes_wr = NO; return_read_eof = NO; /********????????????????*****/ return_write_eof = (status_eot_detected)? YES : NO; /* Clear this in case user app close()d before reading EOF token */ status_eof_detected = NO; reported_read_eof = NO; reported_write_eof = NO; switch (flags & O_ACCMODE) { case O_RDONLY: mode_access = READ; break; case O_WRONLY: /* Fallthru... Strictly speaking this is not correct... */ case O_RDWR: /* Reads are allowed as long as nothing is written */ mode_access = WRITE; break; } /* This is to avoid tape-changed problems (TP_CNI exception). * * Since removing the cartridge will not raise an exception, * we always do a tp_sense() to make sure we have the proper * CNI status, the 2150L may need an additional sense.... - Eddy */ s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); if (s == TE_OK) /* Try to clear cartridge-changed status for Archive-2150L */ if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); if (s != TE_OK) { tpqputs(TPQD_ALWAYS, "open: sense() failed"); return -EIO; } /* exception bits should be up-to-date now, so check for * tape presence and exit if absent. * Even `mt stat' will fail without a tape. */ if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) { tpqputs(TPQD_ALWAYS, "No tape present."); return -EIO; } /* At this point we can assume that a tape is present and * that it will remain present until release() is called. */ /* not allowed to do QCMD_DENS_* unless tape is rewound */ if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) { /* force rewind if minor bits have changed, * i.e. user wants to use tape in different format. * [assuming single drive operation] */ if (TP_HAVE_DENS) { tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind."); need_rewind = YES; } } else { /* density bits still the same, but TP_DIAGS bit * may have changed. */ current_tape_dev = dev; } if (need_rewind == YES) { /***************** CHECK THIS!!!!!!!! **********/ s = do_qic_cmd(QCMD_REWIND, TIM_R); if (s != 0) { tpqputs(TPQD_ALWAYS, "open: rewind failed"); return -EIO; } } /* Note: After a reset command, the controller will rewind the tape * just before performing any tape movement operation! ************ SO SET need_rewind flag!!!!! */ if (status_dead==YES) { tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset"); if (tape_reset(1)!=TE_OK) { return -ENXIO; } else { status_dead = NO; if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) { tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n"); status_dead = YES; /* try reset next time */ return -EIO; } } } /* things should be ok, once we get here */ /* set density: only allowed when TP_BOM status bit is set, * so we must have done a rewind by now. If not, just skip over. * Only give set density command when minor bits have changed. */ if (TP_DENS(current_tape_dev) == TP_DENS(dev) ) return 0; current_tape_dev = dev; need_rewind = NO; if (TP_HAVE_DENS) dens = TP_DENS(dev); if (dens < sizeof(format_names)/sizeof(char *)) printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]); else tpqputs(TPQD_REWIND, "Wait for retensioning..."); switch (TP_DENS(dev)) { case 0: /* Minor 0 is for drives without set-density support */ s = 0; break; case 1: s = do_qic_cmd(QCMD_DENS_11, TIM_S); break; case 2: s = do_qic_cmd(QCMD_DENS_24, TIM_S); break; case 3: s = do_qic_cmd(QCMD_DENS_120, TIM_S); break; case 4: s = do_qic_cmd(QCMD_DENS_150, TIM_S); break; case 5: s = do_qic_cmd(QCMD_DENS_300, TIM_S); break; case 6: s = do_qic_cmd(QCMD_DENS_600, TIM_S); break; default: /* otherwise do a retension before anything else */ s = do_qic_cmd(QCMD_RETEN, TIM_R); } if (s != 0) { status_dead = YES; /* force reset */ current_tape_dev = 0; /* earlier 0xff80 */ return -EIO; } return 0; } /* qic02_tape_open */ static void qic02_tape_release(struct inode * inode, struct file * filp) { kdev_t dev = inode->i_rdev; if (TP_DIAGS(dev)) printk("qic02_tape_release: dev=%s\n", kdevname(dev)); if (status_zombie==YES) /* don't rewind in zombie mode */ return; /* Terminate any pending write cycle. Terminating the read-cycle * is delayed until it is required to do so for a new command. */ terminate_write(-1); if (status_dead==YES) tpqputs(TPQD_ALWAYS, "release: device dead!?"); /* Rewind only if minor number requires it AND * read/writes have been done. ************* IS THIS CORRECT?????????? */ if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) { tpqputs(TPQD_REWIND, "release: Doing rewind..."); (void) do_qic_cmd(QCMD_REWIND, TIM_R); } return; } /* qic02_tape_release */ #ifdef CONFIG_QIC02_DYNCONF /* Set masks etc. based on the interface card type. */ int update_ifc_masks(int ifc) { QIC02_TAPE_IFC = ifc; if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) { QIC02_STAT_PORT = QIC02_TAPE_PORT; QIC02_CTL_PORT = QIC02_TAPE_PORT; QIC02_CMD_PORT = QIC02_TAPE_PORT+1; QIC02_DATA_PORT = QIC02_TAPE_PORT+1; QIC02_STAT_READY = WT_QIC02_STAT_READY; QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION; QIC02_STAT_MASK = WT_QIC02_STAT_MASK; QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK; QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL; QIC02_CTL_RESET = WT_QIC02_CTL_RESET; QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST; if (QIC02_TAPE_DMA == 3) WT_CTL_DMA = WT_CTL_DMA3; else if (QIC02_TAPE_DMA == 1) WT_CTL_DMA = WT_CTL_DMA1; else { tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); return -EIO; } if (QIC02_TAPE_IFC == EVEREX) { /* Everex is a special case for Wangtek (actually * it's the other way 'round, but I saw Wangtek first) */ if (QIC02_TAPE_DMA==3) WT_CTL_DMA = WT_CTL_DMA1; /* Fixup the kernel copy of the IFC type to that * we don't have to distinguish between Wangtek and * and Everex at runtime. */ QIC02_TAPE_IFC = WANGTEK; } } else if (QIC02_TAPE_IFC == ARCHIVE) { QIC02_STAT_PORT = QIC02_TAPE_PORT+1; QIC02_CTL_PORT = QIC02_TAPE_PORT+1; QIC02_CMD_PORT = QIC02_TAPE_PORT; QIC02_DATA_PORT = QIC02_TAPE_PORT; QIC02_STAT_READY = AR_QIC02_STAT_READY; QIC02_STAT_EXCEPTION = AR_QIC02_STAT_EXCEPTION; QIC02_STAT_MASK = AR_QIC02_STAT_MASK; QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK; QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL; QIC02_CTL_RESET = AR_QIC02_CTL_RESET; QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST; if (QIC02_TAPE_DMA > 3) { tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); return -EIO; } } else if (QIC02_TAPE_IFC == MOUNTAIN) { QIC02_STAT_PORT = QIC02_TAPE_PORT+1; QIC02_CTL_PORT = QIC02_TAPE_PORT+1; QIC02_CMD_PORT = QIC02_TAPE_PORT; QIC02_DATA_PORT = QIC02_TAPE_PORT; QIC02_STAT_READY = MTN_QIC02_STAT_READY; QIC02_STAT_EXCEPTION = MTN_QIC02_STAT_EXCEPTION; QIC02_STAT_MASK = MTN_QIC02_STAT_MASK; QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK; QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL; QIC02_CTL_RESET = MTN_QIC02_CTL_RESET; QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST; if (QIC02_TAPE_DMA > 3) { tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); return -EIO; } } else { tpqputs(TPQD_ALWAYS, "Invalid interface type"); return -ENXIO; } return 0; } /* update_ifc-masks */ #endif /* ioctl allows user programs to rewind the tape and stuff like that */ static int qic02_tape_ioctl(struct inode * inode, struct file * filp, unsigned int iocmd, unsigned long ioarg) { int error; short i; int dev_maj = MAJOR(inode->i_rdev); int c; struct mtop operation; char *stp, *argp; unsigned char blk_addr[6]; struct mtpos ioctl_tell; if (TP_DIAGS(current_tape_dev)) printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg); if (!inode || !ioarg) return -EINVAL; /* check iocmd first */ if (dev_maj != QIC02_TAPE_MAJOR) { printk(TPQIC02_NAME ": Oops! Wrong device?\n"); /* A panic() would be appropriate here */ return -ENODEV; } c = iocmd & IOCCMD_MASK; #ifdef DDIOCSDBG /* Check for DDI Debug Control, contributed by FvK, edited by HHB. */ if (c == DDIOCSDBG) { if (!suser()) return -EPERM; error = verify_area(VERIFY_READ, (int *) ioarg, sizeof(int)); if (error) return error; c = get_user((int *) ioarg); if (c==0) { QIC02_TAPE_DEBUG = 0; return 0; } if ((c>=1) && (c<=32)) { QIC02_TAPE_DEBUG |= (1 << (c-1)); return 0; } if (c >= 128) { QIC02_TAPE_DEBUG &= ~(1 << (c - 128)); return 0; } return -EINVAL; } #endif #ifdef CONFIG_QIC02_DYNCONF if (c == (MTIOCGETCONFIG & IOCCMD_MASK)) { if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) { tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!"); return -EFAULT; } /* check for valid user address */ error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(qic02_tape_dynconf)); if (error) return error; /* copy current settings to user space */ stp = (char *) &qic02_tape_dynconf; argp = (char *) ioarg; for (i=0; i<sizeof(qic02_tape_dynconf); i++) put_user(*stp++, argp++); return 0; } else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) { static int qic02_get_resources(void), qic02_release_resources(void); /* One should always do a MTIOCGETCONFIG first, then update * user-settings, then write back with MTIOCSETCONFIG. * Re-open() the device before actual use to make sure * everything is initialized. */ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) { tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!"); return -EFAULT; } if (!suser()) return -EPERM; if ((doing_read!=NO) || (doing_write!=NO)) return -EBUSY; error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(qic02_tape_dynconf)); if (error) return error; /* copy struct from user space to kernel space */ stp = (char *) &qic02_tape_dynconf; argp = (char *) ioarg; for (i=0; i<sizeof(qic02_tape_dynconf); i++) *stp++ = get_user(argp++); if (status_zombie==NO) qic02_release_resources(); /* and go zombie */ if (update_ifc_masks(qic02_tape_dynconf.ifc_type)) return -EIO; if (qic02_get_resources()) return -ENXIO; return 0; } if (status_zombie==YES) { tpqputs(TPQD_ALWAYS, "Configs not set"); return -ENXIO; } #endif if (c == (MTIOCTOP & IOCCMD_MASK)) { /* Compare expected struct size and actual struct size. This * is useful to catch programs compiled with old #includes. */ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtop)) { tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!"); return -EFAULT; } error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(operation)); if (error) return error; /* copy mtop struct from user space to kernel space */ stp = (char *) &operation; argp = (char *) ioarg; for (i=0; i<sizeof(operation); i++) *stp++ = get_user(argp++); /* ---note: mt_count is signed, negative seeks must be * --- translated to seeks in opposite direction! * (only needed for Sun-programs, I think.) */ /* ---note: MTFSF with count 0 should position the * --- tape at the beginning of the current file. */ if (TP_DIAGS(current_tape_dev)) printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count); if (operation.mt_count < 0) tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored"); ioctl_status.mt_resid = operation.mt_count; if (operation.mt_op == MTSEEK) { if (!TP_HAVE_SEEK) return -ENOTTY; seek_addr_buf[0] = (operation.mt_count>>16)&0xff; seek_addr_buf[1] = (operation.mt_count>>8)&0xff; seek_addr_buf[2] = (operation.mt_count)&0xff; if (operation.mt_count>>24) return -EINVAL; if ((error = do_ioctl_cmd(operation.mt_op)) != 0) return error; ioctl_status.mt_resid = 0; } else { while (operation.mt_count > 0) { operation.mt_count--; if ((error = do_ioctl_cmd(operation.mt_op)) != 0) return error; ioctl_status.mt_resid = operation.mt_count; } } return 0; } else if (c == (MTIOCGET & IOCCMD_MASK)) { if (TP_DIAGS(current_tape_dev)) printk("GET "); /* compare expected struct size and actual struct size */ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) { tpqputs(TPQD_ALWAYS, "sizeof(struct mtget) does not match!"); return -EFAULT; } /* check for valid user address */ error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(ioctl_status)); if (error) return error; /* It appears (gmt(1)) that it is normal behaviour to * first set the status with MTNOP, and then to read * it out with MTIOCGET */ /* copy results to user space */ stp = (char *) &ioctl_status; argp = (char *) ioarg; for (i=0; i<sizeof(ioctl_status); i++) put_user(*stp++, argp++); return 0; } else if (TP_HAVE_TELL && (c == (MTIOCPOS & IOCCMD_MASK))) { if (TP_DIAGS(current_tape_dev)) printk("POS "); /* compare expected struct size and actual struct size */ if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) { tpqputs(TPQD_ALWAYS, "sizeof(struct mtpos) does not match!"); return -EFAULT; } /* check for valid user address */ error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(ioctl_tell)); if (error) return error; tpqputs(TPQD_IOCTLS, "MTTELL reading block address"); if ((doing_read==YES) || (doing_write==YES)) finish_rw(AR_QCMDV_TELL_BLK); c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK); if (c!=TE_OK) return -EIO; ioctl_tell.mt_blkno = (blk_addr[3] << 16) | (blk_addr[4] << 8) | blk_addr[5]; /* copy results to user space */ stp = (char *) &ioctl_tell; argp = (char *) ioarg; for (i=0; i<sizeof(ioctl_tell); i++) put_user(*stp++, argp++); return 0; } else return -ENOTTY; /* Other cmds not supported. */ } /* qic02_tape_ioctl */ /* These are (most) of the interface functions: */ static struct file_operations qic02_tape_fops = { qic02_tape_lseek, /* not allowed */ qic02_tape_read, /* read */ qic02_tape_write, /* write */ NULL, /* readdir not allowed */ NULL, /* select ??? */ qic02_tape_ioctl, /* ioctl */ NULL, /* mmap not allowed */ qic02_tape_open, /* open */ qic02_tape_release, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL /* revalidate */ }; /* align `a' at `size' bytes. `size' must be a power of 2 */ static inline unsigned long const align_buffer(unsigned long a, unsigned size) { if (a & (size-1)) /* if not aligned */ return (a | (size-1)) + 1; else /* else is aligned */ return a; } static void qic02_release_resources(void) { free_irq(QIC02_TAPE_IRQ, NULL); free_dma(QIC02_TAPE_DMA); status_zombie = YES; } /* qic02_release_resources */ static int qic02_get_resources(void) { /* First perform some checks. If one of them fails, * the tape driver will not be registered to the system. */ if (QIC02_TAPE_IRQ>16) { tpqputs(TPQD_ALWAYS, "Bogus interrupt number."); return -1; } /* for DYNCONF, allocating DMA & IRQ should not be done until * the config parameters have been set using MTSETCONFIG. */ /* get IRQ */ if (request_irq(QIC02_TAPE_IRQ, qic02_tape_interrupt, SA_INTERRUPT, "QIC-02", NULL)) { printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n", QIC02_TAPE_IRQ); status_zombie = YES; return -1; } /* After IRQ, allocate DMA channel */ if (request_dma(QIC02_TAPE_DMA,"QIC-02")) { printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n", QIC02_TAPE_DMA); free_irq(QIC02_TAPE_IRQ, NULL); status_zombie = YES; return -1; } printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n", QIC02_TAPE_IRQ, QIC02_TAPE_DMA, ((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))? QIC02_CMD_PORT : QIC02_STAT_PORT, (QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" : ((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek")); if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) { /* No drive detected, so vanish */ tpqputs(TPQD_ALWAYS, "No drive detected -- releasing irq and dma."); status_dead = YES; qic02_release_resources(); return -1; } /* All should be ok now */ status_zombie = NO; return 0; } /* qic02_get_resources */ int qic02_tape_init(void) /* Shouldn't this be a caddr_t ? */ { if (TPSTATSIZE != 6) { printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n"); return -ENODEV; } if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) { printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n"); return -ENODEV; } QIC02_TAPE_DEBUG = TPQD_DEFAULT_FLAGS; current_tape_dev = MKDEV(QIC02_TAPE_MAJOR, 0); #ifndef CONFIG_QIC02_DYNCONF printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n", QIC02_TAPE_IRQ, QIC02_TAPE_DMA, # if QIC02_TAPE_IFC == WANGTEK QIC02_STAT_PORT, "Wangtek", # elif QIC02_TAPE_IFC == ARCHIVE QIC02_CMD_PORT, "Archive", # elif QIC02_TAPE_IFC == MOUNTAIN QIC02_CMD_PORT, "Mountain", # else # error # endif rcs_revision, rcs_date); if (qic02_get_resources()) return -ENODEV; #else printk(TPQIC02_NAME ": Runtime config, %s, %s\n", rcs_revision, rcs_date); QIC02_TAPE_IRQ = BOGUS_IRQ; /* invalid value */ #endif printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF); /* Setup the page-address for the dma transfer. * This assumes a one-to-one identity mapping between * kernel addresses and physical memory. */ buffaddr = align_buffer((unsigned long) &qic02_tape_buf, TAPE_BLKSIZE); printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf); #ifndef CONFIG_MAX_16M if (buffaddr+TPQBUF_SIZE>=0x1000000) { printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n"); return -ENODEV; } #endif /* If we got this far, install driver functions */ if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) { printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR); #ifndef CONFIG_QIC02_DYNCONF free_irq(QIC02_TAPE_IRQ, NULL); free_dma(QIC02_TAPE_DMA); #endif return -ENODEV; } /* prepare timer */ TIMEROFF; timer_table[QIC02_TAPE_TIMER].expires = 0; timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out; #ifndef CONFIG_QIC02_DYNCONF if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) { /* No drive detected, so vanish */ tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation..."); status_dead = YES; free_irq(QIC02_TAPE_IRQ, NULL); free_dma(QIC02_TAPE_DMA); unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); return -ENODEV; } else { if (is_exception()) { tpqputs(TPQD_ALWAYS, "exception detected\n"); (void) tp_sense(TP_WRP|TP_POR|TP_CNI); } } #endif /* initialize generic status for ioctl requests */ ioctl_status.mt_type = QIC02_TAPE_DRIVE; /* MT_IS* id nr */ ioctl_status.mt_resid = 0; /* ---residual count */ ioctl_status.mt_gstat = 0; /* ---generic status */ ioctl_status.mt_erreg = 0; /* not used */ ioctl_status.mt_fileno = 0; /* number of current file on tape */ ioctl_status.mt_blkno = 0; /* number of current (logical) block */ return 0; } /* qic02_tape_init */
Go to most recent revision | Compare with Previous | Blame | View Log