[net.bugs.4bsd] TU-78 tape driver can't handle > 1 device +Fix

kwlalonde@watmath.UUCP (Ken Lalonde) (10/21/85)

Index: 	sys/vaxmba/mt.c 4.2BSD

Description:
	The TU-78 driver confuses logical and physical tape transport unit
	numbers.  Everything works fine if you have one device.
	Extra drives will be configured correctly, but will be unusable.

	The driver accesses one of four tape transport units ("mu"'s) 
	through the "mtncs[]" device register.  The slave routine
	correctly indexes this array with the device "slave" value
	supplied in the config file.  The rest of the driver uses
	the low two bits of the special file minor number.  

Repeat-By:
	Read the code (see use of mtncs register in mtslave() and mtustart()).

Fix:
	Index the mtncs register with the slave value used in mtslave().
	A diff follows; it's long but there's not that much to it.
	I am including code (by Alex White) to
		- return error on attempts to write past end-of-tape.
		  There is an ioctl to get around this (who'd want to?).
		- announce presence of dual-port capability at config time,
		  and disallow simultaneous access to such drives
		  (return value is EBUSY, not EIO).
		- clear the controller on receipt of an unexpected interrupt,
		  to avoid a hung device.
	I think these are worthwhile additions.

*** /usr/distr/4.2/sys/vaxmba/mt.c	Fri Jul 29 10:35:54 1983
--- /sys/vaxmba/mt.c	Mon Oct 21 08:44:21 1985
***************
*** 65,70
  	short	sc_dens;
  	struct	mba_device *sc_mi;
  	int	sc_slave;
  } mu_softc[NMU];
  short	mutomt[NMU];
  

--- 65,71 -----
  	short	sc_dens;
  	struct	mba_device *sc_mi;
  	int	sc_slave;
+ 	char	sc_eotok;
  } mu_softc[NMU];
  short	mutomt[NMU];
  #define MAXMU	4	/* maximum number of mu's per mt */
***************
*** 67,72
  	int	sc_slave;
  } mu_softc[NMU];
  short	mutomt[NMU];
  
  /*
   * Bits for sc_flags.

--- 68,76 -----
  	char	sc_eotok;
  } mu_softc[NMU];
  short	mutomt[NMU];
+ #define MAXMU	4	/* maximum number of mu's per mt */
+ char	mttomu[NMT][MAXMU]; /* map (mt unit, slave number) to logical mu unit */
  char	mtds_bits[] = MTDS_BITS;
  
  /*
   * Bits for sc_flags.
***************
*** 91,96
  	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
  	int s = spl7(), rtn = 0;
  
  	mtaddr->mtas = -1;
  	mtaddr->mtncs[sn] = MT_SENSE|MT_GO;
  	while (mtaddr->mtas == 0)

--- 93,103 -----
  	register struct mtdevice *mtaddr = (struct mtdevice *)mi->mi_drv;
  	int s = spl7(), rtn = 0;
  
+ 	if (sn >= MAXMU) {
+ 		printf("mt%d: slave value %d > max %d\n",
+ 			mi->mi_unit, sn, MAXMU-1);
+ 		return 0;
+ 	}
  	mtaddr->mtas = -1;
  	mtaddr->mtncs[sn] = MT_SENSE|MT_GO;
  	while (mtaddr->mtas == 0)
***************
*** 100,105
  		sc->sc_mi = mi;
  		sc->sc_slave = sn;
  		mutomt[ms->ms_unit] = mi->mi_unit;
  		rtn = 1;
  	}
  	mtaddr->mtas = mtaddr->mtas;

--- 107,113 -----
  		sc->sc_mi = mi;
  		sc->sc_slave = sn;
  		mutomt[ms->ms_unit] = mi->mi_unit;
+ 		mttomu[mi->mi_unit][sn] = ms->ms_unit;
  		rtn = 1;
+  		if(mtaddr->mtdt & MTDT_DRQ)
+  			printf("Dual-ported ");	/* Followed by config msg */
***************
*** 117,123
  	int olddens, dens;
  
  	muunit = MUUNIT(dev);
! 	if (muunit >= NMU || (sc = &mu_softc[muunit])->sc_openf ||
  	    (mi = mtinfo[MTUNIT(dev)]) == 0 || mi->mi_alive == 0)
  		return (ENXIO);
  	olddens = sc->sc_dens;

--- 127,133 -----
  	int olddens, dens;
  
  	muunit = MUUNIT(dev);
! 	if (muunit >= NMU ||
  	    (mi = mtinfo[MTUNIT(dev)]) == 0 || mi->mi_alive == 0)
  		return (ENXIO);
  	if ((sc = &mu_softc[muunit])->sc_openf)
***************
*** 120,125
  	if (muunit >= NMU || (sc = &mu_softc[muunit])->sc_openf ||
  	    (mi = mtinfo[MTUNIT(dev)]) == 0 || mi->mi_alive == 0)
  		return (ENXIO);
  	olddens = sc->sc_dens;
  	dens = sc->sc_dens = (minor(dev)&H_6250BPI) ? MT_GCR : 0;
  	mtcommand(dev, MT_SENSE, 1);

--- 130,137 -----
  	if (muunit >= NMU ||
  	    (mi = mtinfo[MTUNIT(dev)]) == 0 || mi->mi_alive == 0)
  		return (ENXIO);
+ 	if ((sc = &mu_softc[muunit])->sc_openf)
+ 		return (EBUSY);
  	olddens = sc->sc_dens;
  	dens = sc->sc_dens = (minor(dev)&H_6250BPI) ? MT_GCR : 0;
  	mtcommand(dev, MT_SENSE, 1);
***************
*** 124,132
  	dens = sc->sc_dens = (minor(dev)&H_6250BPI) ? MT_GCR : 0;
  	mtcommand(dev, MT_SENSE, 1);
  	sc->sc_dens = olddens;
! 	if ((sc->sc_dsreg & MTDS_ONL) == 0) {
! 		uprintf("mu%d: not online\n", muunit);
! 		return (EIO);
  	}
  	if ((flag&FWRITE) && (sc->sc_dsreg&MTDS_FPT)) {
  		uprintf("mu%d: no write ring\n", muunit);

--- 136,148 -----
  	dens = sc->sc_dens = (minor(dev)&H_6250BPI) ? MT_GCR : 0;
  	mtcommand(dev, MT_SENSE, 1);
  	sc->sc_dens = olddens;
! 	/*
! 	 *  Make sure drive is online, and port-select is us, and only us.
! 	 */
! 	if ((sc->sc_dsreg & (MTDS_ONL|MTDS_AVAIL|MTDS_SHR)) !=
! 			    (MTDS_ONL|MTDS_AVAIL	 )) {
! /*		uprintf("mu%d: not online\n", muunit);*/
! 		return EIO;	/* at Waterloo, EOFFL */
  	}
  	if ((flag&FWRITE) && (sc->sc_dsreg&MTDS_FPT)) {
  /*		uprintf("mu%d: no write ring\n", muunit);*/
***************
*** 155,160
  		mtcommand(dev, MT_CLS|sc->sc_dens, 1);
  	if ((minor(dev)&H_NOREWIND) == 0)
  		mtcommand(dev, MT_REW, 0);
  	sc->sc_openf = 0;
  }
  

--- 171,177 -----
  		mtcommand(dev, MT_CLS|sc->sc_dens, 1);
  	if ((minor(dev)&H_NOREWIND) == 0)
  		mtcommand(dev, MT_REW, 0);
+ 	sc->sc_eotok = 0;
  	sc->sc_openf = 0;
  }
  
***************
*** 237,243
  		if ((bp->b_flags&B_READ)==0)
  			sc->sc_nxrec = bdbtofsb(bp->b_blkno) + 1;
  	} else {
! 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
  			(bp->b_repcnt<<8)|bp->b_command|MT_GO;
  		return (MBU_STARTED);
  	}

--- 254,260 -----
  		if ((bp->b_flags&B_READ)==0)
  			sc->sc_nxrec = bdbtofsb(bp->b_blkno) + 1;
  	} else {
! 		mtaddr->mtncs[sc->sc_slave] =
  			(bp->b_repcnt<<8)|bp->b_command|MT_GO;
  		return (MBU_STARTED);
  	}
***************
*** 242,247
  		return (MBU_STARTED);
  	}
  	if ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) {
  		if (mi->mi_tab.b_errcnt == 2) {
  			mtaddr->mtca = MUUNIT(bp->b_dev);
  		} else {

--- 259,274 -----
  		return (MBU_STARTED);
  	}
  	if ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) {
+ 		/*
+ 		 *  Write operation past eot, make sure eotok is set
+ 		 */
+ 		if((bp->b_flags & B_READ) == 0  &&
+ 		   !sc->sc_eotok &&
+ 		   sc->sc_dsreg & MTDS_EOT) {
+ 			bp->b_error = EIO;	/* at Waterloo, EEOT */
+ 			bp->b_flags |= B_ERROR;
+ 			return(MBU_NEXT);
+ 		}
  		if (mi->mi_tab.b_errcnt == 2) {
  			mtaddr->mtca = sc->sc_slave;
  		} else {
***************
*** 243,249
  	}
  	if ((blkno = sc->sc_blkno) == bdbtofsb(bp->b_blkno)) {
  		if (mi->mi_tab.b_errcnt == 2) {
! 			mtaddr->mtca = MUUNIT(bp->b_dev);
  		} else {
  			mtaddr->mtbc = bp->b_bcount;
  			mtaddr->mtca = (1<<2)|MUUNIT(bp->b_dev);

--- 270,276 -----
  			return(MBU_NEXT);
  		}
  		if (mi->mi_tab.b_errcnt == 2) {
! 			mtaddr->mtca = sc->sc_slave;
  		} else {
  			mtaddr->mtbc = bp->b_bcount;
  			mtaddr->mtca = (1<<2)|sc->sc_slave;
***************
*** 246,252
  			mtaddr->mtca = MUUNIT(bp->b_dev);
  		} else {
  			mtaddr->mtbc = bp->b_bcount;
! 			mtaddr->mtca = (1<<2)|MUUNIT(bp->b_dev);
  		}
  		return (MBU_DODATA);
  	}

--- 273,279 -----
  			mtaddr->mtca = sc->sc_slave;
  		} else {
  			mtaddr->mtbc = bp->b_bcount;
! 			mtaddr->mtca = (1<<2)|sc->sc_slave;
  		}
  		return (MBU_DODATA);
  	}
***************
*** 251,257
  		return (MBU_DODATA);
  	}
  	if (blkno < bdbtofsb(bp->b_blkno))
! 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
  		  (min((unsigned)(bdbtofsb(bp->b_blkno) - blkno), 0377) << 8) |
  			MT_SFORW|MT_GO;
  	else

--- 278,284 -----
  		return (MBU_DODATA);
  	}
  	if (blkno < bdbtofsb(bp->b_blkno))
! 		mtaddr->mtncs[sc->sc_slave] =
  		  (min((unsigned)(bdbtofsb(bp->b_blkno) - blkno), 0377) << 8) |
  			MT_SFORW|MT_GO;
  	else
***************
*** 255,261
  		  (min((unsigned)(bdbtofsb(bp->b_blkno) - blkno), 0377) << 8) |
  			MT_SFORW|MT_GO;
  	else
! 		mtaddr->mtncs[MUUNIT(bp->b_dev)] =
  		  (min((unsigned)(blkno - bdbtofsb(bp->b_blkno)), 0377) << 8) |
  			MT_SREV|MT_GO;
  	return (MBU_STARTED);

--- 282,288 -----
  		  (min((unsigned)(bdbtofsb(bp->b_blkno) - blkno), 0377) << 8) |
  			MT_SFORW|MT_GO;
  	else
! 		mtaddr->mtncs[sc->sc_slave] =
  		  (min((unsigned)(blkno - bdbtofsb(bp->b_blkno)), 0377) << 8) |
  			MT_SREV|MT_GO;
  	return (MBU_STARTED);
***************
*** 285,294
  	register struct mu_softc *sc;
  
  	/* I'M NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
- 	if ((mtaddr->mtca&3) != MUUNIT(bp->b_dev)) {
- 		printf("mt: wrong unit!\n");
- 		mtaddr->mtca = MUUNIT(bp->b_dev);
- 	}
  	sc = &mu_softc[MUUNIT(bp->b_dev)];
  	sc->sc_erreg = mtaddr->mter;
  	if((bp->b_flags & B_READ) == 0)

--- 312,317 -----
  	register struct mu_softc *sc;
  
  	/* I'M NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
  	sc = &mu_softc[MUUNIT(bp->b_dev)];
  	if ((mtaddr->mtca&3) != sc->sc_slave) {
  		printf("mt%d: wrong unit!\n", mi->mi_unit);
***************
*** 290,295
  		mtaddr->mtca = MUUNIT(bp->b_dev);
  	}
  	sc = &mu_softc[MUUNIT(bp->b_dev)];
  	sc->sc_erreg = mtaddr->mter;
  	if((bp->b_flags & B_READ) == 0)
  		sc->sc_flags |= H_WRITTEN;

--- 313,322 -----
  
  	/* I'M NOT SURE IF THIS SHOULD ALWAYS BE THE CASE SO FOR NOW... */
  	sc = &mu_softc[MUUNIT(bp->b_dev)];
+ 	if ((mtaddr->mtca&3) != sc->sc_slave) {
+ 		printf("mt%d: wrong unit!\n", mi->mi_unit);
+ 		mtaddr->mtca = sc->sc_slave;
+ 	}
  	sc->sc_erreg = mtaddr->mter;
  	if((bp->b_flags & B_READ) == 0)
  		sc->sc_flags |= H_WRITTEN;
***************
*** 294,299
  	if((bp->b_flags & B_READ) == 0)
  		sc->sc_flags |= H_WRITTEN;
  	switch (sc->sc_erreg & MTER_INTCODE) {
  	case MTER_DONE:
  	case MTER_LONGREC:
  		if (mi->mi_tab.b_errcnt != 2)

--- 321,329 -----
  	if((bp->b_flags & B_READ) == 0)
  		sc->sc_flags |= H_WRITTEN;
  	switch (sc->sc_erreg & MTER_INTCODE) {
+ 	case MTER_EOT:	/* this record has been written ok */
+ 		/* Blasted thing doesn't update mtds */
+ 		sc->sc_dsreg |= MTDS_EOT;
  	case MTER_DONE:
  	case MTER_LONGREC:
  		if (mi->mi_tab.b_errcnt != 2)
***************
*** 306,312
  		goto err;
  
  	case MTER_TM:
- 	case MTER_EOT:
  		sc->sc_blkno++;
  	err:
  		bp->b_resid = bp->b_bcount;

--- 336,341 -----
  		goto err;
  
  	case MTER_TM:
  		sc->sc_blkno++;
  	err:
  		bp->b_resid = bp->b_bcount;
***************
*** 371,376
  
  	unit = (mtaddr->mtner >> 8) & 3;
  	er = MASKREG(mtaddr->mtner);
  	/* WILL THIS OCCUR IF ANOTHER DRIVE COMES ONLINE? */
  	if (bp == 0 || unit != MUUNIT(bp->b_dev)) {	/* consistency check */
  		if ((er & MTER_INTCODE) != MTER_ONLINE)

--- 422,428 -----
  
  	unit = (mtaddr->mtner >> 8) & 3;
  	er = MASKREG(mtaddr->mtner);
+ 	sc = &mu_softc[mttomu[mi->mi_unit][unit]];
  	/* WILL THIS OCCUR IF ANOTHER DRIVE COMES ONLINE? */
  	if (bp == 0 || unit != sc->sc_slave) {	/* consistency check */
  		if ((er & MTER_INTCODE) != MTER_ONLINE) {
***************
*** 372,380
  	unit = (mtaddr->mtner >> 8) & 3;
  	er = MASKREG(mtaddr->mtner);
  	/* WILL THIS OCCUR IF ANOTHER DRIVE COMES ONLINE? */
! 	if (bp == 0 || unit != MUUNIT(bp->b_dev)) {	/* consistency check */
! 		if ((er & MTER_INTCODE) != MTER_ONLINE)
! 			printf("mt: unit %d random interrupt\n", unit);
  		return (MBN_SKIP);
  	}
  	if (bp == 0)

--- 424,438 -----
  	er = MASKREG(mtaddr->mtner);
  	sc = &mu_softc[mttomu[mi->mi_unit][unit]];
  	/* WILL THIS OCCUR IF ANOTHER DRIVE COMES ONLINE? */
! 	if (bp == 0 || unit != sc->sc_slave) {	/* consistency check */
! 		if ((er & MTER_INTCODE) != MTER_ONLINE) {
! 			printf("mu%d: random interrupt er=%o, ds=%b (issuing controller clear)\n",
! 				unit, er, MASKREG(mtaddr->mtds), mtds_bits);
! 			mtaddr->mtid = MTID_CLR;
! 			DELAY(250);
! 			while ((mtaddr->mtid & MTID_RDY) == 0)
! 				;
! 		}
  		return (MBN_SKIP);
  	}
  
***************
*** 377,382
  			printf("mt: unit %d random interrupt\n", unit);
  		return (MBN_SKIP);
  	}
  	if (bp == 0)
  		return (MBN_SKIP);
  	fc = (mtaddr->mtncs[unit] >> 8) & 0xff;

--- 435,442 -----
  		}
  		return (MBN_SKIP);
  	}
+ #if 0
+	/* redundant */
  	if (bp == 0)
  		return (MBN_SKIP);
+ #endif
***************
*** 380,386
  	if (bp == 0)
  		return (MBN_SKIP);
  	fc = (mtaddr->mtncs[unit] >> 8) & 0xff;
- 	sc = &mu_softc[unit];
  	sc->sc_erreg = er;
  	sc->sc_resid = fc;
  	switch (er & MTER_INTCODE) {

--- 441,446 -----
  		return (MBN_SKIP);
  #endif
  	fc = (mtaddr->mtncs[unit] >> 8) & 0xff;
  	sc->sc_erreg = er;
  	sc->sc_resid = fc;
  	switch (er & MTER_INTCODE) {
***************
*** 385,390
  	sc->sc_resid = fc;
  	switch (er & MTER_INTCODE) {
  	case MTER_DONE:
  		if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
  	done:
  			if (bp->b_command == MT_SENSE)

--- 445,451 -----
  	sc->sc_resid = fc;
  	switch (er & MTER_INTCODE) {
  	case MTER_DONE:
+ 	case MTER_EOT:		/* Returned when MT_CLS past EOT */
  		if (bp == &cmtbuf[MTUNIT(bp->b_dev)]) {
  	done:
  			if (bp->b_command == MT_SENSE)
***************
*** 406,412
  		printf("mu%d: blank tape\n", MUUNIT(bp->b_dev));
  
  	case MTER_TM:
- 	case MTER_EOT:
  	case MTER_LEOT:
  		if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) {
  			sc->sc_nxrec = bdbtofsb(bp->b_blkno) + fc;

--- 467,472 -----
  		printf("mu%d: blank tape\n", MUUNIT(bp->b_dev));
  
  	case MTER_TM:
  	case MTER_LEOT:
  		if (sc->sc_blkno > bdbtofsb(bp->b_blkno)) {
  			sc->sc_nxrec = bdbtofsb(bp->b_blkno) + fc;
***************
*** 511,516
  	{MT_WTM,MT_SFORWF,MT_SREVF,MT_SFORW,MT_SREV,MT_REW,MT_UNLOAD,MT_SENSE};
  
  	switch (cmd) {
  
  	case MTIOCTOP:	/* tape operation */
  		mtop = (struct mtop *)data;

--- 571,579 -----
  	{MT_WTM,MT_SFORWF,MT_SREVF,MT_SFORW,MT_SREV,MT_REW,MT_UNLOAD,MT_SENSE};
  
  	switch (cmd) {
+#ifdef MTIOCEOT
+ 	case MTIOCEOT:	/* Allow crossing of eot mark */
+ 		sc->sc_eotok = 1;
+ 		return 0;
+#endif
  
  	case MTIOCTOP:	/* tape operation */
  		mtop = (struct mtop *)data;
***************
*** 532,537
  			break;
  
  		case MTREW: case MTOFFL:
  			callcount = 1;
  			fcount = 1;
  			break;

--- 595,602 -----
  			break;
  
  		case MTREW: case MTOFFL:
+ 			sc->sc_eotok = 0;
  			callcount = 1;
  			fcount = 1;
  			break;