patch-2.4.4 linux/drivers/s390/char/tape34xx.c

Next file: linux/drivers/s390/char/tape34xx.h
Previous file: linux/drivers/s390/char/tape3490.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/drivers/s390/char/tape34xx.c linux/drivers/s390/char/tape34xx.c
@@ -3,12 +3,11 @@
  *  drivers/s390/char/tape34xx.c
  *    common tape device discipline for 34xx tapes.
  *
- *  S390 version
- *    Copyright (C) 2000 IBM Corporation
- *    Author(s): Tuan Ngo-Anh <ngoanh@de.ibm.com>
- *               Carsten Otte <cotte@de.ibm.com>
+ *  S390 and zSeries version
+ *    Copyright (C) 2001 IBM Corporation
+ *    Author(s): Carsten Otte <cotte@de.ibm.com>
+ *               Tuan Ngo-Anh <ngoanh@de.ibm.com>
  *
- *  UNDER CONSTRUCTION: Work in progress...:-)
  ****************************************************************************
  */
 
@@ -36,19 +35,19 @@
 tape_event_handler_t tape34xx_event_handler_table[TS_SIZE][TE_SIZE] =
 {
     /* {START , DONE, FAILED, ERROR, OTHER } */
-	{NULL, tape34xx_unused_done, NULL, tape34xx_unused_error, NULL},	/* TS_UNUSED */
-	{NULL, tape34xx_idle_done, NULL, tape34xx_idle_error, NULL},	/* TS_IDLE */
+	{NULL, tape34xx_unused_done, NULL, NULL, NULL},	/* TS_UNUSED */
+	{NULL, tape34xx_idle_done, NULL, NULL, NULL},	/* TS_IDLE */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_DONE */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_FAILED */
-	{NULL, tape34xx_block_done, NULL, tape34xx_block_error, NULL},		/* TS_BLOCK_INIT */
+	{NULL, tape34xx_block_done, NULL, NULL, NULL},		/* TS_BLOCK_INIT */
 	{NULL, tape34xx_bsb_init_done, NULL, NULL, NULL},	/* TS_BSB_INIT */
 	{NULL, tape34xx_bsf_init_done, NULL, NULL, NULL},	/* TS_BSF_INIT */
 	{NULL, tape34xx_dse_init_done, NULL, NULL, NULL},	/* TS_DSE_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_EGA_INIT */
 	{NULL, tape34xx_fsb_init_done, NULL, NULL, NULL},	/* TS_FSB_INIT */
-	{NULL, tape34xx_fsf_init_done, NULL, tape34xx_fsf_init_error, NULL},	/* TS_FSF_INIT */
+	{NULL, tape34xx_fsf_init_done, NULL, NULL, NULL},	/* TS_FSF_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_LDI_INIT */
-	{NULL, tape34xx_lbl_init_done, NULL, tape34xx_lbl_init_error, NULL},	/* TS_LBL_INIT */
+	{NULL, tape34xx_lbl_init_done, NULL, NULL, NULL},	/* TS_LBL_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_MSE_INIT */
 	{NULL, tape34xx_nop_init_done, NULL, NULL, NULL},	/* TS_NOP_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBA_INIT */
@@ -56,11 +55,11 @@
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBU_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBL_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_RDC_INIT */
-	{NULL, tape34xx_rfo_init_done, NULL, tape34xx_rfo_init_error, NULL},	/* TS_RFO_INIT */
+	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},	/* TS_RFO_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_RSD_INIT */
-	{NULL, tape34xx_rew_init_done, NULL, tape34xx_rew_init_error, NULL},	/* TS_REW_INIT */
-	{NULL, tape34xx_rew_release_init_done, NULL, tape34xx_rew_release_init_error, NULL},	/* TS_REW_RELEASE_IMIT */
-	{NULL, tape34xx_run_init_done, NULL, tape34xx_run_init_error, NULL},	/* TS_RUN_INIT */
+	{NULL, tape34xx_rew_init_done, NULL, NULL, NULL},	/* TS_REW_INIT */
+	{NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL},	/* TS_REW_RELEASE_IMIT */
+	{NULL, tape34xx_run_init_done, NULL, NULL, NULL},	/* TS_RUN_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_SEN_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_SID_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_SNP_INIT */
@@ -70,8 +69,8 @@
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_SYN_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_TIO_INIT */
 	{NULL, NULL, NULL, NULL, NULL},		/* TS_UNA_INIT */
-	{NULL, tape34xx_wri_init_done, NULL, tape34xx_wri_init_error, NULL},	/* TS_WRI_INIT */
-	{NULL, tape34xx_wtm_init_done, NULL, tape34xx_wtm_init_error, NULL},	/* TS_WTM_INIT */
+	{NULL, tape34xx_wri_init_done, NULL, NULL, NULL},	/* TS_WRI_INIT */
+	{NULL, tape34xx_wtm_init_done, NULL, NULL, NULL},	/* TS_WTM_INIT */
 	{NULL, NULL, NULL, NULL, NULL}};        /* TS_NOT_OPER */
 
 
@@ -205,6 +204,59 @@
 #endif /* TAPE_DEBUG */
 	return cqr;
 }
+ccw_req_t *
+tape34xx_read_opposite (tape_info_t * tape,int novalue)
+{
+	ccw_req_t *cqr;
+	ccw1_t *ccw;
+	size_t count;
+	// first, retrieve the count from the old cqr.
+	cqr = tape->cqr;
+	ccw = cqr->cpaddr;
+	ccw++;
+	count=ccw->count;
+	// free old cqr.
+	clear_normalized_cda (ccw);
+	tape_free_request (cqr);
+	// build new cqr
+	cqr = tape_alloc_ccw_req (tape, 3, 0);
+	if (!cqr) {
+#ifdef TAPE_DEBUG
+	        debug_text_exception (tape_debug_area,6,"xrop nomem");
+#endif /* TAPE_DEBUG */
+		return NULL;
+	}
+	ccw = cqr->cpaddr;
+	ccw->cmd_code = MODE_SET_DB;
+	ccw->flags = CCW_FLAG_CC;
+	ccw->count = 1;
+	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) tape->discdata)->modeset_byte)));
+	ccw++;
+
+	ccw->cmd_code = READ_BACKWARD;
+	ccw->flags = CCW_FLAG_CC;
+	ccw->count = count;
+	set_normalized_cda (ccw, (unsigned long) tape->kernbuf);
+	if ((ccw->cda) == 0) {
+		tape_free_request (cqr);
+		return NULL;
+	}
+	ccw++;
+	ccw->cmd_code = FORSPACEBLOCK;
+	ccw->flags = CCW_FLAG_CC;
+	ccw->count = 1;
+	ccw->cda = (unsigned long)ccw;
+	ccw++;
+	ccw->cmd_code = NOP;
+	ccw->flags = 0;
+	ccw->count = 1;
+	ccw->cda = (unsigned long)ccw;
+	tapestate_set (tape, TS_RBA_INIT);
+#ifdef TAPE_DEBUG
+	debug_text_event (tape_debug_area,6,"xrop ccwg");
+#endif /* TAPE_DEBUG */
+	return cqr;
+}
 
 void 
 tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * tape)
@@ -233,7 +285,6 @@
 #endif /* TAPE_DEBUG */
 }
 
-
 /*
  * The IOCTL interface is implemented in the following section,
  * excepted the MTRESET, MTSETBLK which are handled by tapechar.c
@@ -1329,7 +1380,7 @@
 		else
 			bhct++;
 	}
-	if ((data = kmalloc (4 * sizeof (__u8), GFP_KERNEL)) == NULL) {
+	if ((data = kmalloc (4 * sizeof (__u8), GFP_ATOMIC)) == NULL) {
 #ifdef TAPE_DEBUG
 	        debug_text_exception (tape_debug_area,3,"xBREDnomem");
 #endif /* TAPE_DEBUG */
@@ -1395,9 +1446,8 @@
 					    size,
 					    blksize_size[tapeblock_major][tape->blk_minor],
 					    req->nr_sectors);
-
-				tape_free_request (cqr);
 				kfree(data);
+				tape_free_request (cqr);
 				return NULL;
 			}
 		}
@@ -1506,17 +1556,6 @@
 	}
 }
 
-void
-tape34xx_unused_error (tape_info_t * tape)
-{
-#ifdef TAPE_DEBUG
-    debug_text_event (tape_debug_area,3,"unsol.irq!");
-    debug_text_event (tape_debug_area,3,"unit chk!");
-    debug_int_exception (tape_debug_area,3,tape->devinfo.irq);
-#endif /* TAPE_DEBUG */
-    PRINT_WARN ("Unsolicited IRQ (Unit Check) caught in unused state.\n");
-    tape_dump_sense (&tape->devstat);
-}
 
 void
 tape34xx_idle_done (tape_info_t * tape)
@@ -1531,18 +1570,6 @@
 }
 
 void
-tape34xx_idle_error (tape_info_t * tape)
-{
-#ifdef TAPE_DEBUG
-        debug_text_event (tape_debug_area,3,"unsol.irq!");
-	debug_text_event (tape_debug_area,3,"unit chk!");
-	debug_int_exception (tape_debug_area,3,tape->devinfo.irq);
-#endif /* TAPE_DEBUG */
-	PRINT_WARN ("Unsolicited IRQ (Unit Check) caught in idle state.\n");
-	tape_dump_sense (&tape->devstat);
-}
-
-void
 tape34xx_block_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1553,16 +1580,6 @@
 }
 
 void
-tape34xx_block_error (tape_info_t * tape)
-{
-#ifdef TAPE_DEBUG
-        debug_text_event (tape_debug_area,3,"x:xREQfail");
-#endif /* TAPE_DEBUG */
-	tapestate_set(tape,TS_FAILED);
-	schedule_tapeblock_exec_IO(tape);
-}
-
-void
 tape34xx_bsf_init_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1599,27 +1616,6 @@
 }
 
 void
-tape34xx_fsf_init_error (tape_info_t * tape)
-{
-	if (((tape->devstat.ii.sense.data[0] == 0x08) ||	// sense.data[0]=08 -> first time
-	      (tape->devstat.ii.sense.data[0] == 0x10) ||	// an alternate one...
-	      (tape->devstat.ii.sense.data[0] == 0x12)) &&	// sense.data[1]=12 -> repeated message
-	     (tape->devstat.ii.sense.data[1] == 0x40)) {
-		// end of recorded area!
-#ifdef TAPE_DEBUG
-        debug_text_event (tape_debug_area,3,"fsf fail");
-        debug_text_exception (tape_debug_area,3,"eoRecArea");
-#endif	/* TAPE_DEBUG */
-		tape->rc = -EIO;
-		tapestate_set (tape, TS_FAILED);
-		tape->wanna_wakeup=1;
-		wake_up_interruptible (&tape->wq);
-	} else {
-		tape34xx_unexpect_uchk_handler (tape);
-	}
-}
-
-void
 tape34xx_fsb_init_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1657,29 +1653,6 @@
 }
 
 void
-tape34xx_lbl_init_error (tape_info_t * tape)
-{
-	if (((tape->devstat.ii.sense.data[0] == 0x00) ||	// sense.data[0]=00 -> first time
-	      (tape->devstat.ii.sense.data[0] == 0x08) ||	// an alternate one...
-	     (tape->devstat.ii.sense.data[0] == 0x10) ||        // alternate, too
-	      (tape->devstat.ii.sense.data[0] == 0x12)) &&	// sense.data[1]=12 -> repeated message
-	     ((tape->devstat.ii.sense.data[1] == 0x40) ||
-	      (tape->devstat.ii.sense.data[1] == 0xc0))) {
-		// block not found!
-#ifdef TAPE_DEBUG
-        debug_text_event (tape_debug_area,3,"lbl fail");
-        debug_text_exception (tape_debug_area,3,"blk nfound");
-#endif	/* TAPE_DEBUG */
-		tape->rc = -EIO;
-		tapestate_set (tape, TS_FAILED);
-		tape->wanna_wakeup=1;
-		wake_up_interruptible (&tape->wq);
-	} else {
-		tape34xx_unexpect_uchk_handler (tape);
-	}
-}
-
-void
 tape34xx_nop_init_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1699,11 +1672,8 @@
 #ifdef TAPE_DEBUG
         debug_text_event (tape_debug_area,6,"rfo done");
 #endif
-	//BH: use irqsave
-	//s390irq_spin_lock(tape->devinfo.irq);
 	tapestate_set (tape, TS_DONE);
 	tape->rc = 0;
-	//s390irq_spin_unlock(tape->devinfo.irq);
 	tape->wanna_wakeup=1;
 	wake_up_interruptible (&tape->wq);
 }
@@ -1729,50 +1699,6 @@
 }
 
 void
-tape34xx_rfo_init_error (tape_info_t * tape)
-{
-	if (((tape->devstat.ii.sense.data[0] == 0x08) ||	// sense.data[0]=08 -> first time
-	      (tape->devstat.ii.sense.data[0] == 0x10) ||	// an alternate one...
-	      (tape->devstat.ii.sense.data[0] == 0x12)) &&	// sense.data[1]=12 -> repeated message
-	     (tape->devstat.ii.sense.data[1] == 0x40)) {
-		// end of recorded area!
-#ifdef TAPE_DEBUG
-        debug_text_event (tape_debug_area,3,"rfo fail");
-        debug_text_exception (tape_debug_area,3,"eoRecArea");
-#endif	/* TAPE_DEBUG */
-		tape->rc = 0;
-		tapestate_set (tape, TS_FAILED);
-		tape->wanna_wakeup=1;
-		wake_up_interruptible (&tape->wq);
-	} else {
-		switch (tape->devstat.ii.sense.data[3]) {
-		case 0x48:
-#ifdef TAPE_DEBUG
-		        debug_text_event (tape_debug_area,3,"rfo fail");
-                        debug_text_exception (tape_debug_area,3,"recov x48");
-#endif	/* TAPE_DEBUG */
-			//s390irq_spin_lock(tape->devinfo.irq);
-			do_IO (tape->devinfo.irq, tape->cqr->cpaddr, (unsigned long) (tape->cqr), 0x00, tape->cqr->options);
-			//s390irq_spin_unlock(tape->devinfo.irq);
-			break;
-		case 0x2c:
-			PRINT_ERR ("TAPE: Permanent Unit Check. Please check your hardware!");
-#ifdef TAPE_DEBUG
-			debug_text_event (tape_debug_area,3,"rfo fail");
-			debug_text_exception (tape_debug_area,3,"Perm UCK");
-#endif
-			tape->rc = -EIO;
-			tapestate_set (tape, TS_FAILED);
-			tape->wanna_wakeup=1;
-			wake_up_interruptible (&tape->wq);
-			break;
-		default:
-			tape34xx_unexpect_uchk_handler (tape);
-		}
-	}
-}
-
-void
 tape34xx_rew_init_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1788,44 +1714,6 @@
 }
 
 void
-tape34xx_rew_release_init_error (tape_info_t * tape)
-{
-	if ((tape->devstat.ii.sense.data[0] == 0x40) &&
-	    (tape->devstat.ii.sense.data[1] == 0x40) &&
-	    (tape->devstat.ii.sense.data[3] == 0x43)) {
-		// no tape in the drive
-		PRINT_INFO ("Drive %d not ready. No volume loaded.\n", tape->rew_minor / 2);
-#ifdef TAPE_DEBUG
-		debug_text_event (tape_debug_area,3,"rewR fail");
-		debug_text_exception (tape_debug_area,3,"no medium");
-#endif
-		tapestate_set (tape, TS_FAILED);
-		tape->rc = -ENOMEDIUM;
-		tape->wanna_wakeup=1;
-		wake_up (&tape->wq);
-	} else {
-		PRINT_ERR ("TAPE34XX: An unexpected Unit Check occurred.\n");
-		PRINT_ERR ("TAPE34XX: Please send the following 20 lines of output to cotte@de.ibm.com\n");
-		PRINT_ERR ("TAPE34XX: Current state is: %s",
-			   (((tapestate_get (tape) < TS_SIZE) && (tapestate_get (tape) >= 0)) ?
-		  state_verbose[tapestate_get (tape)] : "->UNKNOWN STATE<-"));
-		tapestate_set (tape, TS_FAILED);
-#ifdef TAPE_DEBUG
-	        debug_text_event (tape_debug_area,3,"rewR unexp");
-	        debug_text_event (tape_debug_area,3,"state:");
-	        debug_text_event (tape_debug_area,3,((tapestate_get (tape) < TS_SIZE) && 
-						     (tapestate_get (tape) >= 0)) ?
-				  state_verbose[tapestate_get (tape)] : 
-				  "TS UNKNOWN");
-#endif /* TAPE_DEBUG */
-		tape_dump_sense (&tape->devstat);
-		tape->rc = -EIO;
-		tape->wanna_wakeup=1;
-		wake_up (&tape->wq);
-	}
-}
-
-void
 tape34xx_rew_release_init_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1839,12 +1727,6 @@
 }
 
 void
-tape34xx_rew_init_error (tape_info_t * tape)
-{
-	tape34xx_unexpect_uchk_handler (tape);
-}
-
-void
 tape34xx_run_init_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1857,27 +1739,6 @@
 }
 
 void
-tape34xx_run_init_error (tape_info_t * tape)
-{
-
-	switch (tape->devstat.ii.sense.data[3]) {
-	case 0x52:
-	        // This error is fine for rewind and unload
-	        // It reports that no volume is loaded... 
-#ifdef TAPE_DEBUG
-	        debug_text_event (tape_debug_area,6,"run done");
-#endif	/* TAPE_DEBUG */
-		tapestate_set (tape, TS_DONE);
-		tape->rc = 0;
-		tape->wanna_wakeup=1;
-		wake_up_interruptible (&tape->wq);
-		break;
-	default:
-		tape34xx_unexpect_uchk_handler (tape);
-	}
-}
-
-void
 tape34xx_wri_init_done (tape_info_t * tape)
 {
 #ifdef TAPE_DEBUG
@@ -1893,72 +1754,632 @@
 }
 
 void
-tape34xx_wri_init_error (tape_info_t * tape)
+tape34xx_wtm_init_done (tape_info_t * tape)
 {
-    if ((tape->devstat.ii.sense.data[0]==0x80)&&(tape->devstat.ii.sense.data[1]==0x4a)) {
-	// tape is write protected
 #ifdef TAPE_DEBUG
-        debug_text_event (tape_debug_area,3,"wri fail");
-        debug_text_exception (tape_debug_area,3,"writProte");
-#endif	/* TAPE_DEBUG */
-	tape->rc = -EACCES;
-	tapestate_set (tape, TS_FAILED);
+        debug_text_event (tape_debug_area,3,"wtm done");
+#endif
+	tapestate_set (tape, TS_DONE);
+	tape->rc = 0;
 	tape->wanna_wakeup=1;
 	wake_up_interruptible (&tape->wq);
-    } else {
-	switch (tape->devstat.ii.sense.data[3]) {
-	case 0x48:
-#ifdef TAPE_DEBUG
-	        debug_text_event (tape_debug_area,3,"wri fail");
-		debug_text_exception (tape_debug_area,3,"recov x48");
-#endif	/* TAPE_DEBUG */
-		//s390irq_spin_lock(tape->devinfo.irq);
-		do_IO (tape->devinfo.irq, tape->cqr->cpaddr, (unsigned long) (tape->cqr), 0x00, tape->cqr->options);
-		//s390irq_spin_unlock(tape->devinfo.irq);
-		break;
-	case 0x2c:
-		PRINT_ERR ("TAPE: Permanent Unit Check. Please check your hardware!\n");
-#ifdef TAPE_DEBUG
-		debug_text_event (tape_debug_area,3,"wri fail");
-		debug_text_exception (tape_debug_area,3,"Perm UCK");
-#endif
-		tape->rc = -EIO;
-		tapestate_set (tape, TS_FAILED);
-		tape->wanna_wakeup=1;
-		wake_up_interruptible (&tape->wq);
-		break;
-	case 0x38:		//end of tape
-#ifdef TAPE_DEBUG
-		PRINT_WARN ("TAPE: End of Tape reached.\n");
-		debug_text_event (tape_debug_area,3,"wri fail");
-		debug_text_exception (tape_debug_area,3,"EOT!");
-#endif
-		tape->rc = tape->devstat.rescnt;
-		tapestate_set (tape, TS_FAILED);
-		tape->wanna_wakeup=1;
-		wake_up_interruptible (&tape->wq);
-		break;
+}
+
+/* This function analyses the tape's sense-data in case of a unit-check. If possible,
+   it tries to recover from the error. Else the user is informed about the problem. */
+void
+tape34xx_error_recovery (tape_info_t* tape)
+{
+    __u8* sense=tape->devstat.ii.sense.data;
+    int inhibit_cu_recovery=0;
+    int cu_type=tape->discipline->cu_type;
+    if ((((tape34xx_disc_data_t *) tape->discdata)->modeset_byte)&0x80) inhibit_cu_recovery=1;
+    if (tapestate_get(tape)==TS_BLOCK_INIT) {
+	// no recovery for block device, bottom half will retry...
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    }
+    if (sense[0]&SENSE_COMMAND_REJECT)
+	switch (tapestate_get(tape)) {
+	case TS_BLOCK_INIT:
+	case TS_DSE_INIT:
+	case TS_EGA_INIT:
+	case TS_WRI_INIT:
+	case TS_WTM_INIT:
+	    if (sense[1]&SENSE_WRITE_PROTECT) {
+		// trying to write, but medium is write protected
+		tape34xx_error_recovery_has_failed(tape,EACCES);
+		return;
+	    }
 	default:
-		tape34xx_unexpect_uchk_handler (tape);
+	    tape34xx_error_recovery_HWBUG(tape,1);
+	    return;
+	}
+    // special cases for various tape-states when reaching end of recorded area
+    if (((sense[0]==0x08) || (sense[0]==0x10) || (sense[0]==0x12)) &&
+	((sense[1]==0x40) || (sense[1]==0x0c)))
+	switch (tapestate_get(tape)) {
+	case TS_FSF_INIT:
+	    // Trying to seek beyond end of recorded area
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	case TS_LBL_INIT:
+	    // Block could not be located.
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	case TS_RFO_INIT:
+	    // Try to read beyond end of recorded area -> 0 bytes read
+	    tape34xx_error_recovery_has_failed(tape,0);
+	    return;
+	}
+    // Sensing special bits
+    if (sense[0]&SENSE_BUS_OUT_CHECK) {
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    }
+    if (sense[0]&SENSE_DATA_CHECK) {
+	// hardware failure, damaged tape or improper operating conditions
+	switch (sense[3]) {
+	case 0x23:
+	    // a read data check occurred
+	    if ((sense[2]&SENSE_TAPE_SYNC_MODE) ||
+		(inhibit_cu_recovery)) {
+		// data check is not permanent, may be recovered. 
+		// We always use async-mode with cu-recovery, so this should *never* happen.
+		tape34xx_error_recovery_HWBUG(tape,2);
+		return;
+	    } else {
+		// data check is permanent, CU recovery has failed
+		PRINT_WARN("Permanent read error, recovery failed!\n");
+		tape34xx_error_recovery_has_failed(tape,EIO);
+		return;
+	    }
+	case 0x25:
+	    // a write data check occurred
+	    if ((sense[2]&SENSE_TAPE_SYNC_MODE) ||
+		(inhibit_cu_recovery)) {
+		// data check is not permanent, may be recovered.
+		// We always use async-mode with cu-recovery, so this should *never* happen.
+		tape34xx_error_recovery_HWBUG(tape,3);
+		return;
+	    } else {
+		// data check is permanent, cu-recovery has failed
+		PRINT_WARN("Permanent write error, recovery failed!\n");
+		tape34xx_error_recovery_has_failed(tape,EIO);
+		return;
+	    }
+	case 0x26:
+	    // Data Check (read opposite) occurred. We'll recover this.
+	    tape34xx_error_recovery_read_opposite(tape);
+	    return;
+	case 0x28:
+	    // The ID-Mark at the beginning of the tape could not be written. This is fatal, we'll report and exit.
+	    PRINT_WARN("ID-Mark could not be written. Check your hardware!\n");
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	case 0x31:
+	    // Tape void. Tried to read beyond end of device. We'll report and exit.
+	    PRINT_WARN("Try to read beyond end of recorded area!\n");
+	    tape34xx_error_recovery_has_failed(tape,ENOSPC);
+	    return;
+	case 0x41:
+	    // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit.
+	    PRINT_WARN("Illegal block-id sequence found!\n");
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	    default:
+	    // well, all data checks for 3480 should result in one of the above erpa-codes. if not -> bug
+	    // On 3490, other data-check conditions do exist.
+		if (cu_type==0x3480) {
+		    tape34xx_error_recovery_HWBUG(tape,4);
+		    return;
+		}
 	}
     }
+    if (sense[0]&SENSE_OVERRUN) {
+	// A data overrun between cu and drive occurred. The channel speed is to slow! We'll report this and exit!
+	switch (sense[3]) {
+	case 0x40: // overrun error
+	    PRINT_WARN ("Data overrun error between control-unit and drive. Use a faster channel connection, if possible! \n");
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	default:
+	    // Overrun bit is set, but erpa does not show overrun error. This is a bug.
+	    tape34xx_error_recovery_HWBUG(tape,5);
+	    return;
+	}
+    }
+    if (sense[1]&SENSE_RECORD_SEQUENCE_ERR) {
+	switch (sense[3]) {
+	case 0x41:
+	    // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit.
+	    PRINT_WARN("Illegal block-id sequence found!\n");
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	default:
+	    // Record sequence error bit is set, but erpa does not show record sequence error. This is a bug.
+	    tape34xx_error_recovery_HWBUG(tape,6);
+	    return;
+	}
+    }
+    // Sensing erpa codes
+    switch (sense[3]) {
+    case 0x00:
+	// Everything is fine, but we got a unit check. Report and ignore!
+	PRINT_WARN ("Non-error sense was found. Unit-check will be ignored, expect errors...\n");
+	return;
+    case 0x21:
+	// Data streaming not operational. Cu switches to interlock mode, we reissue the command.
+	PRINT_WARN ("Data streaming not operational. Switching to interlock-mode! \n");
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x22:
+	// Path equipment check. Might be drive adapter error, buffer error on the lower interface, internal path not useable, or error during cartridge load.
+	// All of the above are not recoverable
+	PRINT_WARN ("A path equipment check occurred. One of the following conditions occurred:\n");
+	PRINT_WARN ("drive adapter error,buffer error on the lower interface, internal path not useable, error during cartridge load.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x23:
+	// Read data check. Should have been be covered earlier -> Bug!
+	tape34xx_error_recovery_HWBUG(tape,7);
+	return;
+    case 0x24:
+	// Load display check. Load display was command was issued, but the drive is displaying a drive check message. Can be threated as "device end".
+	tape34xx_error_recovery_succeded(tape);
+	return;
+    case 0x25:
+	// Write data check. Should have been covered earlier -> Bug!
+	tape34xx_error_recovery_HWBUG(tape,8);
+	return;
+    case 0x26:
+	// Data check (read opposite). Should have been covered earlier -> Bug!
+	tape34xx_error_recovery_HWBUG(tape,9);
+	return;
+    case 0x27:
+	// Command reject. May indicate illegal channel program or buffer over/underrun. 
+	// Since all channel programms are issued by this driver and ought be correct,
+	// we assume a over/underrun situaltion and retry the channel program.
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x28:
+	// Write id mark check. Should have beed covered earlier -> bug!
+	tape34xx_error_recovery_HWBUG(tape,10);
+	return;
+    case 0x29:
+	// Function incompatible. Either idrc is on but hardware not capable doing idrc 
+	// or a perform subsystem func is issued and the cu is not online. Anyway, this 
+	// cannot be recovered and is an I/O error.
+	PRINT_WARN ("Function incompatible. Try to switch off idrc! \n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x2a:
+	// Unsolicited environmental data. An internal counter overflows, we can ignore
+	// this and reissue the cmd.
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x2b:
+	// Environmental data present. Indicates either unload completed ok or read buffered 
+	// log command completed ok. 
+	if (tapestate_get(tape)==TS_RUN_INIT) {
+	    // Rewind unload completed ok.
+	    tape34xx_error_recovery_succeded(tape);
+	    return;
+	}
+	// Since we do not issue read buffered log commands, this should never occur -> bug.
+	tape34xx_error_recovery_HWBUG(tape,11);
+	return;
+    case 0x2c:
+	// Permanent equipment check. cu has tried recovery, but did not succeed. This is an
+	// I/O error.
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x2d:
+	// Data security erase failure.
+	if (tapestate_get(tape)==TS_DSE_INIT) {
+	    // report an I/O error
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	}
+	// Data security erase failure, but no such command issued. This is a bug.
+	tape34xx_error_recovery_HWBUG(tape,12);
+	return;
+    case 0x2e:
+	// Not capable. This indicates either that the drive fails reading the format id mark
+	// or that that format specified is not supported by the drive. We write a message and
+	// return an I/O error.
+	PRINT_WARN("Drive not capable processing the tape format!");
+	tape34xx_error_recovery_has_failed(tape,EMEDIUMTYPE);
+	return;
+    case 0x2f:
+	// This erpa is reserved. This is a bug.
+	tape34xx_error_recovery_HWBUG(tape,13);
+	return;
+    case 0x30:
+	// The medium is write protected, while trying to write on it. We'll report this.
+	PRINT_WARN("Medium is write protected!\n");
+	tape34xx_error_recovery_has_failed(tape,EACCES);
+	return;
+    case 0x31:
+	// Tape void. Should have beed covered ealier -> bug
+	tape34xx_error_recovery_HWBUG(tape,14);
+	return;
+    case 0x32:
+	// Tension loss. We cannot recover this, it's an I/O error.
+	PRINT_WARN("The drive lost tape tension.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x33:
+	// Load Failure. The catridge was not inserted correctly or the tape is not threaded
+	// correctly. We cannot recover this, the user has to reload the catridge.
+	PRINT_WARN("Cartridge load failure. Reload the cartridge and try again.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x34:
+	// Unload failure. The drive cannot maintain tape tension and control tape movement 
+	// during an unload operation. 
+	PRINT_WARN("Failure during cartridge unload. Please try manually.\n");
+	if (tapestate_get(tape)!=TS_RUN_INIT) {
+	    tape34xx_error_recovery_HWBUG(tape,15);
+	    return;
+	}
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x35:
+	// Drive equipment check. One of the following:
+	// - cu cannot recover from a drive detected error
+	// - a check code message is displayed on drive message/load displays
+	// - the cartridge loader does not respond correctly
+	// - a failure occurs during an index, load, or unload cycle
+	PRINT_WARN("Equipment check! Please check the drive and the cartridge loader.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x36:
+	switch (cu_type) {
+	case 0x3480:
+	    // This erpa is reserved for 3480 -> BUG
+	    tape34xx_error_recovery_HWBUG(tape,16);
+	    return;
+	case 0x3490:
+	    // End of data. This is a permanent I/O error, which cannot be recovered.
+	    // A read-type command has reached the end-of-data mark.
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	    return;
+	}
+    case 0x37:
+	// Tape length error. The tape is shorter than reported in the beginning-of-tape data.
+	PRINT_WARN("Tape length error.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x38:
+	// Physical end of tape. A read/write operation reached the physical end of tape.
+	if (tapestate_get(tape)==TS_WRI_INIT) {
+ 	    tape34xx_error_recovery_has_failed(tape,ENOSPC);
+	}
+	return;
+    case 0x39:
+	// Backward at BOT. The drive is at BOT and is requestet to move backward.
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x3a:
+	// Drive switched not ready, but the command needs the drive to be ready.
+	PRINT_WARN("Drive not ready. Turn the ready/not ready switch to ready position and try again.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x3b:
+	// Manual rewind or unload. This causes an I/O error.
+	PRINT_WARN("Medium is rewinded or unloaded manually.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x3c:
+    case 0x3d:
+    case 0x3e:
+    case 0x3f:
+	// These erpas are reserved -> BUG
+	tape34xx_error_recovery_HWBUG(tape,17);
+	return;
+    case 0x40:
+	// Overrun error. This should have been covered earlier -> bug.
+	tape34xx_error_recovery_HWBUG(tape,18);
+	return;
+    case 0x41:
+	// Record sequence error. This should have been covered earlier -> bug.
+	tape34xx_error_recovery_HWBUG(tape,19);
+	return;
+    case 0x42:
+	// Degraded mode. A condition that can cause degraded performace is detected.
+	PRINT_WARN("Subsystem is running in degraded mode. This may compromise your performace.\n");
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x43:
+	// Drive not ready. Probably swith the ready/not ready switch to ready?
+	PRINT_WARN("The drive is not ready. Maybe no medium in?\n");
+	tape34xx_error_recovery_has_failed(tape,ENOMEDIUM);
+	return;
+    case 0x44:
+	// Locate Block unsuccessfull. We'll report this.
+	if ((tapestate_get(tape)!=TS_BLOCK_INIT) &&
+	    (tapestate_get(tape)!=TS_LBL_INIT)) {
+	    tape34xx_error_recovery_HWBUG(tape,20); // No locate block was issued...
+	    return;
+	}
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x45:
+	// The drive is assigned elsewhere [to a different channel path/computer].
+	PRINT_WARN("The drive is assigned elsewhere.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x46:
+	// Drive not online. Drive may be switched offline, the power supply may be switched off 
+	// or the drive address may not be set correctly.
+	PRINT_WARN("The drive is not online.");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x47:
+	// Volume fenced. cu reports volume integrity is lost! 
+	PRINT_WARN("Volume fenced. The volume integrity is lost! \n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x48:
+	// Log sense data and retry request. We'll do so...
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x49:
+	// Bus out check. A parity check error on the bus was found.	PRINT_WARN("Bus out check. A data transfer over the bus was corrupted.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x4a:
+	// Control unit erp failed. We'll report this.
+	PRINT_WARN("The control unit failed recovering an I/O error.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x4b:
+	// Cu and drive incompatible. The drive requests micro-program patches, which are not available on the cu.
+	PRINT_WARN("The drive needs microprogram patches from the control unit, which are not available.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x4c:
+	// Recovered Check-One failure. Cu develops a hardware error, but is able to recover. We'll reissue the command.
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x4d:
+	switch (cu_type) {
+	case 0x3480:
+	    // This erpa is reserved for 3480 -> bug
+      	    tape34xx_error_recovery_HWBUG(tape,21);
+	    return;
+	case 0x3490:
+	    // Resetting event recieved. Since the driver does not support resetting event recovery
+	    // (which has to be handled by the I/O Layer), we'll report and retry our command.
+	    tape34xx_error_recovery_do_retry(tape);
+	    return;
+	}
+    case 0x4e:
+	switch (cu_type) {
+	case 0x3480:
+	    // This erpa is reserved for 3480 -> bug.
+	    tape34xx_error_recovery_HWBUG(tape,22);
+	    return;
+	case 0x3490:
+	    // Maximum block size exeeded. This indicates, that the block to be written is larger
+	    // than allowed for buffered mode. We'll report this...
+	    PRINT_WARN("Maximum block size for buffered mode exceeded.\n");
+	    tape34xx_error_recovery_has_failed(tape,ENOBUFS);
+	    return;
+	}
+    case 0x4f:
+	// These erpas are reserved -> bug
+	tape34xx_error_recovery_HWBUG(tape,23);
+	return;
+    case 0x50:
+	// Read buffered log (Overflow). Cu is running in extended beffered log mode, and a counter overflows.
+	// This should never happen, since we're never running in extended buffered log mode -> bug.
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x51:
+	// Read buffered log (EOV). EOF processing occurs while the cu is in extended buffered log mode.
+	// This should never happen, since we're never running in extended buffered log mode -> bug.
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x52:
+	// End of Volume complete. Rewind unload completed ok. We'll report to the user...
+	if (tapestate_get(tape)!=TS_RUN_INIT) {
+	    tape34xx_error_recovery_HWBUG(tape,24);
+	    return;
+	}
+	tape34xx_error_recovery_succeded(tape);
+	return;
+    case 0x53:
+	// Global command intercept. We'll have to reissue our command.
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x54:
+	// Channel interface recovery (temporary). This can be recovered by reissuing the command.
+	tape34xx_error_recovery_do_retry(tape);
+	return;
+    case 0x55:
+	// Channel interface recovery (permanent). This cannot be recovered, we'll inform the user.
+	PRINT_WARN("A permanent channel interface error occurred.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x56:
+	// Channel protocol error. This cannot be recovered.
+	PRINT_WARN("A channel protocol error occurred.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x57:
+	switch (cu_type) {
+	case 0x3480:
+	    // Attention intercept. We have to reissue the command.
+	    PRINT_WARN("An attention intercept occurred, which will be recovered.\n");
+	    tape34xx_error_recovery_do_retry(tape);
+	    return;
+	case 0x3490:
+	    // Global status intercept. We have to reissue the command.
+	    PRINT_WARN("An global status intercept was recieved, which will be recovered.\n");
+	    tape34xx_error_recovery_do_retry(tape);
+	    return;
+	}
+    case 0x58:
+    case 0x59:
+	// These erpas are reserved -> bug.
+	tape34xx_error_recovery_HWBUG(tape,25);
+	return;
+    case 0x5a:
+	// Tape length incompatible. The tape inserted is too long, 
+	// which could cause damage to the tape or the drive.
+	PRINT_WARN("Tape length incompatible [should be IBM Cartridge System Tape]. May cause damage to drive or tape.n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x5b:
+	// Format 3480 XF incompatible
+	if (sense[1]&SENSE_BEGINNING_OF_TAPE) {
+	    // Everything is fine. The tape will be overwritten in a different format.
+	    tape34xx_error_recovery_do_retry(tape);
+	    return;
+	}
+	PRINT_WARN("Tape format is incompatible to the drive, which writes 3480-2 XF.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x5c:
+	// Format 3480-2 XF incompatible
+	PRINT_WARN("Tape format is incompatible to the drive. The drive cannot access 3480-2 XF volumes.\n");
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	return;
+    case 0x5d:
+	// Tape length violation. 
+	PRINT_WARN("Tape length violation [should be IBM Enhanced Capacity Cartridge System Tape]. May cause damage to drive or tape.\n");
+	tape34xx_error_recovery_has_failed(tape,EMEDIUMTYPE);
+	return;
+    case 0x5e:
+	// Compaction algorithm incompatible.
+	PRINT_WARN("The volume is recorded using an incompatible compaction algorith, which is not supported by the control unit.\n");
+	tape34xx_error_recovery_has_failed(tape,EMEDIUMTYPE);
+	return;
+    default:
+	// Reserved erpas -> bug
+	tape34xx_error_recovery_HWBUG(tape,26);
+	return;
+    }
 }
 
-void
-tape34xx_wtm_init_done (tape_info_t * tape)
-{
+void tape34xx_error_recovery_has_failed (tape_info_t* tape,int error_id) {
 #ifdef TAPE_DEBUG
-        debug_text_event (tape_debug_area,3,"wtm done");
+    debug_text_event (tape_debug_area,3,"xerp fail");
+    debug_text_event (tape_debug_area,3,(((tapestate_get (tape) < TS_SIZE) && 
+		      (tapestate_get (tape) >= 0)) ?
+	state_verbose[tapestate_get (tape)] : "UNKNOWN"));
 #endif
-	tapestate_set (tape, TS_DONE);
-	tape->rc = 0;
+    if ((tapestate_get(tape)!=TS_UNUSED) && (tapestate_get(tape)!=TS_IDLE)) {
+	tape_dump_sense(&tape->devstat);
+	tape->rc = -error_id;
 	tape->wanna_wakeup=1;
-	wake_up_interruptible (&tape->wq);
+	switch (tapestate_get(tape)) {
+	case TS_REW_RELEASE_INIT:
+	    tapestate_set(tape,TS_FAILED);
+	    wake_up (&tape->wq);
+	    break;
+	case TS_BLOCK_INIT:
+	    tapestate_set(tape,TS_FAILED);
+	    schedule_tapeblock_exec_IO(tape);
+	    break;
+	default:
+	    tapestate_set(tape,TS_FAILED);
+	    wake_up_interruptible (&tape->wq);
+	}
+    } else {
+	PRINT_WARN("Recieved an unsolicited IRQ.\n");
+	tape_dump_sense(&tape->devstat);
+    }
+}    
+
+void tape34xx_error_recovery_succeded(tape_info_t* tape) {
+#ifdef TAPE_DEBUG
+    debug_text_event (tape_debug_area,3,"xerp done");
+    debug_text_event (tape_debug_area,3,(((tapestate_get (tape) < TS_SIZE) && 
+		      (tapestate_get (tape) >= 0)) ?
+	state_verbose[tapestate_get (tape)] : "UNKNOWN"));
+#endif
+    if ((tapestate_get(tape)!=TS_UNUSED) && (tapestate_get(tape)!=TS_DONE)) {
+	tapestate_event (tape, TE_DONE);
+    } else {
+	PRINT_WARN("Recieved an unsolicited IRQ.\n");
+	tape_dump_sense(&tape->devstat);
+    }
 }
 
-void
-tape34xx_wtm_init_error (tape_info_t * tape)
-{
-        tape34xx_unexpect_uchk_handler (tape);
+void tape34xx_error_recovery_do_retry(tape_info_t* tape) {
+#ifdef TAPE_DEBUG
+    debug_text_event (tape_debug_area,3,"xerp retr");
+    debug_text_event (tape_debug_area,3,(((tapestate_get (tape) < TS_SIZE) && 
+					  (tapestate_get (tape) >= 0)) ?
+					 state_verbose[tapestate_get (tape)] : "UNKNOWN"));
+#endif
+    if ((tapestate_get(tape)!=TS_UNUSED) && (tapestate_get(tape)!=TS_IDLE)) {
+	tape_dump_sense(&tape->devstat);
+	while (do_IO (tape->devinfo.irq, tape->cqr->cpaddr, (unsigned long) tape->cqr, 0x00, tape->cqr->options));
+    } else {
+	PRINT_WARN("Recieved an unsolicited IRQ.\n");
+	tape_dump_sense(&tape->devstat);
+    }
+}
     
+void 
+tape34xx_error_recovery_read_opposite (tape_info_t* tape) {
+    switch (tapestate_get(tape)) {
+    case TS_RFO_INIT:
+	// We did read forward, but the data could not be read *correctly*.
+	// We will read backward and then skip forward again.
+	tape->cqr=tape34xx_read_opposite(tape,0);
+	if (tape->cqr==NULL)
+	    tape34xx_error_recovery_has_failed(tape,EIO);
+	else
+	    tape34xx_error_recovery_do_retry(tape);
+	break;
+    case TS_RBA_INIT:
+	// We tried to read forward and backward, but hat no success -> failed.
+	tape34xx_error_recovery_has_failed(tape,EIO);
+	break;
+    case TS_BLOCK_INIT:
+	tape34xx_error_recovery_do_retry(tape);
+	break;
+    default:
+	PRINT_WARN("read_opposite_recovery_called_with_state:%s\n",
+		   (((tapestate_get (tape) < TS_SIZE) && 
+		     (tapestate_get (tape) >= 0)) ?
+		    state_verbose[tapestate_get (tape)] : "UNKNOWN"));
+    }
+}
+
+void 
+tape34xx_error_recovery_HWBUG (tape_info_t* tape,int condno) {
+    devstat_t* stat=&tape->devstat;
+    PRINT_WARN("An unexpected condition #%d was caught in tape error recovery.\n",condno);
+    PRINT_WARN("Please report this incident.\n");
+    PRINT_WARN("State of the tape:%s\n",
+	       (((tapestate_get (tape) < TS_SIZE) && 
+		 (tapestate_get (tape) >= 0)) ?
+		state_verbose[tapestate_get (tape)] : "UNKNOWN"));
+    PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
+		" %02X%02X%02X%02X %02X%02X%02X%02X \n",
+		stat->ii.sense.data[0], stat->ii.sense.data[1],
+		stat->ii.sense.data[2], stat->ii.sense.data[3],
+		stat->ii.sense.data[4], stat->ii.sense.data[5],
+		stat->ii.sense.data[6], stat->ii.sense.data[7],
+		stat->ii.sense.data[8], stat->ii.sense.data[9],
+		stat->ii.sense.data[10], stat->ii.sense.data[11],
+		stat->ii.sense.data[12], stat->ii.sense.data[13],
+		stat->ii.sense.data[14], stat->ii.sense.data[15]);
+    PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
+		" %02X%02X%02X%02X %02X%02X%02X%02X \n",
+		stat->ii.sense.data[16], stat->ii.sense.data[17],
+		stat->ii.sense.data[18], stat->ii.sense.data[19],
+		stat->ii.sense.data[20], stat->ii.sense.data[21],
+		stat->ii.sense.data[22], stat->ii.sense.data[23],
+		stat->ii.sense.data[24], stat->ii.sense.data[25],
+		stat->ii.sense.data[26], stat->ii.sense.data[27],
+		stat->ii.sense.data[28], stat->ii.sense.data[29],
+		stat->ii.sense.data[30], stat->ii.sense.data[31]);
+    tape34xx_error_recovery_has_failed(tape,EIO);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)