Delete some references to sleep() that somehow crept in.
[unix-history] / sys / i386 / isa / ft.c
CommitLineData
df30aeb2
AM
1/*
2 * Copyright (c) 1993 Steve Gerakines
3 *
4 * This is freely redistributable software. You may do anything you
5 * wish with it, so long as the above notice stays intact.
6 *
7 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
8 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
10 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
11 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
12 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
13 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
15 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
16 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
17 * POSSIBILITY OF SUCH DAMAGE.
18 *
19 * ft.c - QIC-40/80 floppy tape driver
ee874879 20 * $Id: ft.c,v 1.4 1994/02/14 22:24:28 nate Exp $
4af7e30c 21 *
df30aeb2
AM
22 *
23 * 01/26/94 v0.3b - Jim Babb
24 * Got rid of the hard coded device selection. Moved (some of) the
25 * static variables into a structure for support of multiple devices.
26 * ( still has a way to go for 2 controllers - but closer )
27 * Changed the interface with fd.c so we no longer 'steal' it's
28 * driver routine vectors.
29 *
30 * 10/30/93 v0.3
31 * Fixed a couple more bugs. Reading was sometimes looping when an
32 * an error such as address-mark-missing was encountered. Both
33 * reading and writing was having more backup-and-retries than was
34 * necessary. Added support to get hardware info. Updated for use
35 * with FreeBSD.
36 *
37 * 09/15/93 v0.2 pl01
38 * Fixed a bunch of bugs: extra isa_dmadone() in async_write() (shouldn't
39 * matter), fixed double buffering in async_req(), changed tape_end() in
40 * set_fdcmode() to reduce unexpected interrupts, changed end of track
41 * processing in async_req(), protected more of ftreq_rw() with an
42 * splbio(). Changed some of the ftreq_*() functions so that they wait
43 * for inactivity and then go, instead of aborting immediately.
44 *
45 * 08/07/93 v0.2 release
46 * Shifted from ftstrat to ioctl support for I/O. Streaming is now much
47 * more reliable. Added internal support for error correction, QIC-40,
48 * and variable length tapes. Random access of segments greatly
49 * improved. Formatting and verification support is close but still
50 * incomplete.
51 *
52 * 06/03/93 v0.1 Alpha release
53 * Hopefully the last re-write. Many bugs fixed, many remain.
54 */
55
56#include "ft.h"
57#if NFT > 0
58#include "fd.h"
59
60#include <sys/param.h>
61#include <sys/dkbad.h>
62#include <sys/systm.h>
63#include <sys/conf.h>
64#include <sys/file.h>
65#include <sys/ioctl.h>
66#include <sys/malloc.h>
67#include <sys/buf.h>
68#include <sys/uio.h>
69#include <sys/ftape.h>
70#include <machine/pio.h>
71#include "i386/isa/isa_device.h"
72#include "i386/isa/fdreg.h"
c2ab4ad1 73#include "i386/isa/fdc.h"
df30aeb2
AM
74#include "i386/isa/icu.h"
75#include "i386/isa/rtc.h"
76#include "ftreg.h"
77
78/* Enable or disable debugging messages. */
79#define FTDBGALL 0 /* everything */
80/* #define DPRT(a) printf a */
81#define DPRT(a)
82
83/* Constants private to the driver */
84#define FTPRI (PRIBIO) /* sleep priority */
85
86/* The following items are needed from the fd driver. */
87extern int in_fdc(int); /* read fdc registers */
88extern int out_fdc(int, int); /* write fdc registers */
89
90extern int hz; /* system clock rate */
91
92/* Type of tape attached */
93/* use numbers that don't interfere with the possible floppy types */
94#define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */
95 /* F_TAPE_TYPE must match value in fd.c */
96#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */
97#define FT_MOUNTAIN (F_TAPE_TYPE | 1)
98#define FT_COLORADO (F_TAPE_TYPE | 2)
99
100
101/* Mode FDC is currently in: tape or disk */
102enum { FDC_TAPE_MODE, FDC_DISK_MODE };
103
104/* Command we are awaiting completion of */
105enum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID };
106
107/* Tape interrupt status of current request */
108enum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT };
109
110/* Tape I/O status */
111enum {
112 FTIO_READY, /* No I/O activity */
113 FTIO_READING, /* Currently reading blocks */
114 FTIO_RDAHEAD, /* Currently reading ahead */
115 FTIO_WRITING /* Buffers are being written */
116};
117
118/* Current tape mode */
119enum {
120 FTM_PRIMARY, /* Primary mode */
121 FTM_VERIFY, /* Verify mode */
122 FTM_FORMAT, /* Format mode */
123 FTM_DIAG1, /* Diagnostic mode 1 */
124 FTM_DIAG2 /* Diagnostic mode 2 */
125};
126
127/* Tape geometries table */
128QIC_Geom ftgtbl[] = {
129 { 0, 0, "Unformatted", "Unknown", 0, 0, 0, 0, 0 }, /* XXX */
130 { 1, 1, "QIC-40", "205/550", 20, 68, 2176, 128, 21760 },
131 { 1, 2, "QIC-40", "307.5/550", 20, 102, 3264, 128, 32640 },
132 { 1, 3, "QIC-40", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
133 { 1, 4, "QIC-40", "1100/550", 20, 365, 11680, 128, 32512 },
134 { 1, 5, "QIC-40", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
135 { 2, 1, "QIC-80", "205/550", 28, 100, 3200, 128, 19200 },
136 { 2, 2, "QIC-80", "307.5/550", 28, 150, 4800, 128, 19200 },
137 { 2, 3, "QIC-80", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
138 { 2, 4, "QIC-80", "1100/550", 28, 537, 17184, 128, 32512 },
139 { 2, 5, "QIC-80", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
140 { 3, 1, "QIC-500", "205/550", 0, 0, 0, 0, 0 }, /* ??? */
141 { 3, 2, "QIC-500", "307.5/550", 0, 0, 0, 0, 0 }, /* ??? */
142 { 3, 3, "QIC-500", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
143 { 3, 4, "QIC-500", "1100/550", 0, 0, 0, 0, 0 }, /* ??? */
144 { 3, 5, "QIC-500", "1100/900", 0, 0, 0, 0, 0 } /* ??? */
145};
146#define NGEOM (sizeof(ftgtbl) / sizeof(QIC_Geom))
147
148QIC_Geom *ftg = NULL; /* Current tape's geometry */
149
150/*
151 * things relating to asynchronous commands
152 */
153static int astk_depth; /* async_cmd stack depth */
154static int awr_state; /* state of async write */
155static int ard_state; /* state of async read */
156static int arq_state; /* state of async request */
157static int async_retries; /* retries, one per invocation */
158static int async_func; /* function to perform */
159static int async_state; /* state current function is at */
160static int async_arg[5]; /* up to 5 arguments for async cmds */
161static int async_ret; /* return value */
162
163/* List of valid async (interrupt driven) tape support functions. */
164enum {
165 ACMD_NONE, /* no command */
166 ACMD_SEEK, /* command seek */
167 ACMD_STATUS, /* report status */
168 ACMD_STATE, /* wait for state bits to be true */
169 ACMD_SEEKSTS, /* perform command and wait for status */
170 ACMD_READID, /* read id */
171 ACMD_RUNBLK /* ready tape for I/O on the given block */
172};
173
174/* Call another asyncronous command from within async_cmd(). */
175#define CALL_ACMD(r,f,a,b,c,d,e) \
176 astk[astk_depth].over_retries = async_retries; \
177 astk[astk_depth].over_func = async_func; \
178 astk[astk_depth].over_state = (r); \
179 for (i = 0; i < 5; i++) \
180 astk[astk_depth].over_arg[i] = async_arg[i]; \
181 async_func = (f); async_state = 0; async_retries = 0; \
182 async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \
183 async_arg[3]=(d); async_arg[4]=(e); \
184 astk_depth++; \
185 goto restate
186
187/* Perform an asyncronous command from outside async_cmd(). */
188#define ACMD_FUNC(r,f,a,b,c,d,e) over_async = (r); astk_depth = 0; \
189 async_func = (f); async_state = 0; async_retries = 0; \
190 async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \
191 async_arg[3]=(d); async_arg[4]=(e); \
192 async_cmd(ftu); \
193 return
194
195/* Various wait channels */
196static struct {
197 int buff_avail;
198 int iosts_change;
199 int long_delay;
200 int intr_wait;
201} ftsem;
202
203/***********************************************************************\
204* Per controller structure. *
205\***********************************************************************/
206extern struct fdc_data fdc_data[NFDC];
207
208/***********************************************************************\
209* Per tape drive structure. *
210\***********************************************************************/
211struct ft_data {
212 struct fdc_data *fdc; /* pointer to controller structure */
213 int ftsu; /* this units number on this controller */
214 int type; /* Drive type (Mountain, Colorado) */
215/* QIC_Geom *ftg; */ /* pointer to Current tape's geometry */
216 int flags;
217 int cmd_wait; /* Command we are awaiting completion of */
218 int sts_wait; /* Tape interrupt status of current request */
219 int io_sts; /* Tape I/O status */
220 int mode;
221 int pcn; /* present cylinder number */
222 int attaching; /* true when ft is attaching */
223 unsigned char *xptr; /* pointer to buffer blk to xfer */
224 int xcnt; /* transfer count */
225 int xblk; /* block number to transfer */
226 SegReq *curseg; /* Current segment to do I/O on */
227 SegReq *bufseg; /* Buffered segment to r/w ahead */
228 /* the next 3 should be defines in 'flags' */
229 int active; /* TRUE if transfer is active */
230 int rdonly; /* TRUE if tape is read-only */
231 int newcart; /* TRUE if new cartridge detected */
232 int laststs; /* last reported status code */
233 int lastcfg; /* last reported QIC config */
234 int lasterr; /* last QIC error code */
235 int lastpos; /* last known segment number */
236 int moving; /* TRUE if tape is moving */
237 int rid[7]; /* read_id return values */
238
239} ft_data[NFT];
240
241/***********************************************************************\
242* Throughout this file the following conventions will be used: *
243* ft is a pointer to the ft_data struct for the drive in question *
244* fdc is a pointer to the fdc_data struct for the controller *
245* ftu is the tape drive unit number *
246* fdcu is the floppy controller unit number *
247* ftsu is the tape drive unit number on that controller. (sub-unit) *
248\***********************************************************************/
249
250
251typedef int ftu_t;
252typedef int ftsu_t;
253typedef struct ft_data *ft_p;
254
255#define id_physid id_scsiid /* this biotab field doubles as a field */
256 /* for the physical unit number on the controller */
257
258int ftopen(dev_t, int);
259int ftclose(dev_t, int);
260void ftstrategy(struct buf *);
261int ftioctl(dev_t, int, caddr_t, int, struct proc *);
262int ftdump(dev_t);
263int ftsize(dev_t);
264static void ft_timeout(caddr_t arg1, int arg2);
265void async_cmd(ftu_t);
266void async_req(ftu_t, int);
267void async_read(ftu_t, int);
268void async_write(ftu_t, int);
269void tape_start(ftu_t);
270void tape_end(ftu_t);
271void tape_inactive(ftu_t);
272
273
274
275
276
277/*
278 * Probe/attach floppy tapes.
279 */
280int ftattach(isadev, fdup)
281 struct isa_device *isadev, *fdup;
282{
283 fdcu_t fdcu = isadev->id_unit; /* fdc active unit */
284 fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */
285 ftu_t ftu = fdup->id_unit;
c2ab4ad1 286 ft_p ft;
df30aeb2
AM
287 ftsu_t ftsu = fdup->id_physid;
288
c2ab4ad1
AM
289 if (ftu >= NFT)
290 return 0;
291 ft = &ft_data[ftu];
df30aeb2
AM
292 /* Probe for tape */
293 ft->attaching = 1;
294 ft->type = NO_TYPE;
295 ft->fdc = fdc;
296 ft->ftsu = ftsu;
297
298 tape_start(ftu); /* ready controller for tape */
299 tape_cmd(ftu, QC_COL_ENABLE1);
300 tape_cmd(ftu, QC_COL_ENABLE2);
301 if (tape_status(ftu) >= 0) {
302 ft->type = FT_COLORADO;
303 fdc->flags |= FDC_HASFTAPE;
304 printf(" [%d: ft%d: Colorado tape]",
305 fdup->id_physid, fdup->id_unit );
306 tape_cmd(ftu, QC_COL_DISABLE);
307 goto out;
308 }
309
310 tape_start(ftu); /* ready controller for tape */
311 tape_cmd(ftu, QC_MTN_ENABLE1);
312 tape_cmd(ftu, QC_MTN_ENABLE2);
313 if (tape_status(ftu) >= 0) {
314 ft->type = FT_MOUNTAIN;
315 fdc->flags |= FDC_HASFTAPE;
316 printf(" [%d: ft%d: Mountain tape]",
317 fdup->id_physid, fdup->id_unit );
318 tape_cmd(ftu, QC_MTN_DISABLE);
319 goto out;
320 }
321
322out:
323 tape_end(ftu);
324 ft->attaching = 0;
325 return(ft->type);
326}
327
328
329/*
330 * Perform common commands asynchronously.
331 */
332void async_cmd(ftu_t ftu) {
333 ft_p ft = &ft_data[ftu];
334 fdcu_t fdcu = ft->fdc->fdcu;
335 int cmd, i, st0, st3, pcn;
336 static int bitn, retval, retpos, nbits, newcn;
337 static struct {
338 int over_func;
339 int over_state;
340 int over_retries;
341 int over_arg[5];
342 } astk[15];
343 static int wanttrk, wantblk, wantdir;
344 static int curpos, curtrk, curblk, curdir, curdiff;
345 static int errcnt = 0;
346
347restate:
348#if FTDBGALL
349 DPRT(("async_cmd state: func: %d state: %d\n", async_func, async_state));
350#endif
351 switch(async_func) {
352 case ACMD_SEEK:
353 /*
354 * Arguments:
355 * 0 - command to perform
356 */
357 switch (async_state) {
358 case 0:
359 cmd = async_arg[0];
360#if FTDBGALL
361 DPRT(("===>async_seek cmd = %d\n", cmd));
362#endif
363 newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
364 async_state = 1;
365 i = 0;
366 if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1;
367 if (!i && out_fdc(fdcu, 0x00) < 0) i = 1;
368 if (!i && out_fdc(fdcu, newcn) < 0) i = 1;
369 if (i) {
370 if (++async_retries >= 10) {
371 DPRT(("ft%d: async_cmd command seek failed!!\n", ftu));
372 goto complete;
373 }
374 DPRT(("ft%d: async_cmd command seek retry...\n",ftu));
375 async_state = 0;
376 goto restate;
377 }
378 break;
379 case 1:
380 out_fdc(fdcu, NE7CMD_SENSEI);
381 st0 = in_fdc(fdcu);
382 pcn = in_fdc(fdcu);
383 if (st0 < 0 || pcn < 0 || newcn != pcn) {
384 if (++async_retries >= 10) {
385 DPRT(("ft%d: async_cmd seek retries exceeded\n",ftu));
386 goto complete;
387 }
388 DPRT(("ft%d: async_cmd command bad st0=$%02x pcn=$%02x\n",
389 ftu, st0, pcn));
390 async_state = 0;
391 timeout(ft_timeout, (caddr_t)ftu, hz/10);
392 break;
393 }
394 if (st0 & 0x20) { /* seek done */
395 ft->pcn = pcn;
396 }
397#if FTDBGALL
398 else
399 DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n",
400 ftu, st0, pcn));
401#endif
402 if (async_arg[1]) goto complete;
403 async_state = 2;
404 timeout(ft_timeout, (caddr_t)ftu, hz/50);
405 break;
406 case 2:
407 goto complete;
408 /* NOTREACHED */
409 }
410 break;
411
412 case ACMD_STATUS:
413 /*
414 * Arguments:
415 * 0 - command to issue report from
416 * 1 - number of bits
417 * modifies: bitn, retval, st3
418 */
419 switch (async_state) {
420 case 0:
421 bitn = 0;
422 retval = 0;
423 cmd = async_arg[0];
424 nbits = async_arg[1];
425 DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits));
426 CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
427 /* NOTREACHED */
428 case 1:
429 out_fdc(fdcu, NE7CMD_SENSED);
430 out_fdc(fdcu, 0x00);
431 st3 = in_fdc(fdcu);
432 if (st3 < 0) {
433 DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n",
434 ftu,bitn,retval));
435 async_ret = -1;
436 goto complete;
437 }
438 if ((st3 & 0x10) != 0) retval |= (1 << bitn);
439 bitn++;
440 if (bitn >= (nbits+2)) {
441 if ((retval & 1) && (retval & (1 << (nbits+1)))) {
442 async_ret = (retval & ~(1<<(nbits+1))) >> 1;
443 if (async_arg[0] == QC_STATUS && async_arg[2] == 0 &&
444 (async_ret & (QS_ERROR|QS_NEWCART))) {
445 async_state = 2;
446 goto restate;
447 }
448 DPRT(("async status got $%04x ($%04x)\n", async_ret,retval));
449 } else {
450 DPRT(("ft%d: async_status failed: retval=$%04x nbits=%d\n",
451 ftu, retval,nbits));
452 async_ret = -2;
453 }
454 goto complete;
455 }
456 CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
457 /* NOTREACHED */
458 case 2:
459 if (async_ret & QS_NEWCART) ft->newcart = 1;
460 CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1, 0, 0);
461 case 3:
462 ft->lasterr = async_ret;
463 if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) {
464 DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
465 ftu, ft->lasterr & 0xff, ft->lasterr >> 8));
466 }
467 cmd = async_arg[0];
468 nbits = async_arg[1];
469 CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0);
470 case 4:
471 goto complete;
472 case 5:
473 CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
474 case 6:
475 CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
476 case 7:
477 CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0);
478 case 8:
479 cmd = async_arg[0];
480 CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0);
481 }
482 break;
483
484 case ACMD_STATE:
485 /*
486 * Arguments:
487 * 0 - status bits to check
488 */
489 switch(async_state) {
490 case 0:
491 CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
492 case 1:
493 if ((async_ret & async_arg[0]) != 0) goto complete;
494 async_state = 0;
495 if (++async_retries == 360) { /* 90 secs. */
496 DPRT(("ft%d: acmd_state exceeded retry count\n", ftu));
497 goto complete;
498 }
499 timeout(ft_timeout, (caddr_t)ftu, hz/4);
500 break;
501 }
502 break;
503
504 case ACMD_SEEKSTS:
505 /*
506 * Arguments:
507 * 0 - command to perform
508 * 1 - status bits to check
509 * 2 - (optional) seconds to wait until completion
510 */
511 switch(async_state) {
512 case 0:
513 cmd = async_arg[0];
514 async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10;
515 CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0);
516 case 1:
517 CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
518 case 2:
519 if ((async_ret & async_arg[1]) != 0) goto complete;
520 if (--async_retries == 0) {
521 DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu));
522 goto complete;
523 }
524 async_state = 1;
525 timeout(ft_timeout, (caddr_t)ftu, hz/4);
526 break;
527 }
528 break;
529
530 case ACMD_READID:
531 /*
532 * Arguments: (none)
533 */
534 switch(async_state) {
535 case 0:
536 if (!ft->moving) {
537 CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
538 /* NOTREACHED */
539 }
540 async_state = 1;
541 out_fdc(fdcu, 0x4a); /* READ_ID */
542 out_fdc(fdcu, 0);
543 break;
544 case 1:
545 for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
546 async_ret = (ft->rid[3]*ftg->g_fdtrk) +
547 (ft->rid[4]*ftg->g_fdside) + ft->rid[5] - 1;
548 DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
549 ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3],
550 ft->rid[4], ft->rid[5], async_ret));
551 if ((ft->rid[0] & 0xc0) == 0x40) {
552 if (++errcnt >= 10) {
553 DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu));
554 async_ret = ft->lastpos;
555 errcnt = 0;
556 goto complete;
557 }
558 if (errcnt > 2) {
559 ft->moving = 0;
560 CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
561 }
562 DPRT(("readid retry...\n"));
563 async_state = 0;
564 goto restate;
565 }
566 if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) {
567 DPRT(("acmd_readid detected last block on track\n"));
568 retpos = async_ret;
569 CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0);
570 /* NOTREACHED */
571 }
572 ft->lastpos = async_ret;
573 errcnt = 0;
574 goto complete;
575 /* NOTREACHED */
576 case 2:
577 CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
578 case 3:
579 ft->moving = 0;
580 async_ret = retpos+1;
581 goto complete;
582 case 4:
583 CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
584 case 5:
585 ft->moving = 1;
586 async_state = 0;
587 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
588 break;
589 }
590 break;
591
592 case ACMD_RUNBLK:
593 /*
594 * Arguments:
595 * 0 - block number I/O will be performed on
596 *
597 * modifies: curpos
598 */
599 switch (async_state) {
600 case 0:
601 wanttrk = async_arg[0] / ftg->g_blktrk;
602 wantblk = async_arg[0] % ftg->g_blktrk;
603 wantdir = wanttrk & 1;
604 ft->moving = 0;
605 CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
606 case 1:
607 curtrk = wanttrk;
608 curdir = curtrk & 1;
609 DPRT(("Changing to track %d\n", wanttrk));
610 CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0);
611 case 2:
612 cmd = wanttrk+2;
613 CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
614 case 3:
615 CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0);
616 case 4:
617 ft->laststs = async_ret;
618 if (wantblk == 0) {
619 curblk = 0;
620 cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART;
621 CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90, 0, 0);
622 }
623 if (ft->laststs & QS_BOT) {
624 DPRT(("Tape is at BOT\n"));
625 curblk = (wantdir) ? 4800 : 0;
626 async_state = 6;
627 goto restate;
628 }
629 if (ft->laststs & QS_EOT) {
630 DPRT(("Tape is at EOT\n"));
631 curblk = (wantdir) ? 0 : 4800;
632 async_state = 6;
633 goto restate;
634 }
635 CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
636 case 5:
637 curtrk = (async_ret+1) / ftg->g_blktrk;
638 curblk = (async_ret+1) % ftg->g_blktrk;
639 DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n",
640 curtrk, wanttrk, curblk, wantblk));
641 if (curtrk != wanttrk) { /* oops! */
642 DPRT(("oops!! wrong track!\n"));
643 CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
644 }
645 async_state = 6;
646 goto restate;
647 case 6:
648 DPRT(("curtrk = %d nextblk = %d\n", curtrk, curblk));
649 if (curblk == wantblk) {
650 ft->lastpos = curblk - 1;
651 async_ret = ft->lastpos;
652 if (ft->moving) goto complete;
653 CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0);
654 }
655 if (curblk > wantblk) { /* passed it */
656 ft->moving = 0;
657 CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
658 }
659 if ((wantblk - curblk) <= 96) { /* approaching it */
660 CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
661 }
662 /* way up ahead */
663 ft->moving = 0;
664 CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
665 break;
666 case 7:
667 ft->moving = 1;
668 CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
669 break;
670 case 8:
671 async_state = 9;
672 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
673 break;
674 case 9:
675 goto complete;
676 case 10:
677 curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2;
678 if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1;
679 DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff));
680 CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0, 0, 0);
681 case 11:
682 DPRT(("reverse 1 done\n"));
683 CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0);
684 case 12:
685 DPRT(("reverse 2 done\n"));
686 CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0);
687 case 13:
688 CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0);
689 case 14:
690 curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2;
691 if (curdiff < 0) curdiff = 0;
692 DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff));
693 CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0, 0, 0);
694 case 15:
695 DPRT(("forward 1 done\n"));
696 CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0);
697 case 16:
698 DPRT(("forward 2 done\n"));
699 CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0);
700 }
701 break;
702 }
703
704 return;
705
706complete:
707 if (astk_depth) {
708 astk_depth--;
709 async_retries = astk[astk_depth].over_retries;
710 async_func = astk[astk_depth].over_func;
711 async_state = astk[astk_depth].over_state;
712 for(i = 0; i < 5; i++)
713 async_arg[i] = astk[astk_depth].over_arg[i];
714 goto restate;
715 }
716 async_func = ACMD_NONE;
717 async_state = 0;
718 switch (ft->io_sts) {
719 case FTIO_READY:
720 async_req(ftu, 2);
721 break;
722 case FTIO_READING:
723 async_read(ftu, 2);
724 break;
725 case FTIO_WRITING:
726 async_write(ftu, 2);
727 break;
728 default:
729 DPRT(("ft%d: bad async_cmd ending I/O state!\n", ftu));
730 break;
731 }
732}
733
734
735/*
736 * Entry point for the async request processor.
737 */
738void async_req(ftu_t ftu, int from)
739{
740 ft_p ft = &ft_data[ftu];
741 SegReq *sp;
742 static int over_async, lastreq, domore;
743 int cmd;
744
745 if (from == 2) arq_state = over_async;
746
747restate:
748 switch (arq_state) {
749 case 0: /* Process segment */
750 ft->io_sts = ft->curseg->reqtype;
751 if (ft->io_sts == FTIO_WRITING)
752 async_write(ftu, from);
753 else
754 async_read(ftu, from);
755 if (ft->io_sts != FTIO_READY) return;
756
757 /* Swap buffered and current segment */
758 lastreq = ft->curseg->reqtype;
759 ft->curseg->reqtype = FTIO_READY;
760 sp = ft->curseg;
761 ft->curseg = ft->bufseg;
762 ft->bufseg = sp;
763
764 wakeup((caddr_t)&ftsem.buff_avail);
765
766 /* Detect end of track */
767 if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) {
768 domore = (ft->curseg->reqtype != FTIO_READY);
769 ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0);
770 }
771 arq_state = 1;
772 goto restate;
773
774 case 1: /* Next request */
775 if (ft->curseg->reqtype != FTIO_READY) {
776 ft->curseg->reqcrc = 0;
777 arq_state = ard_state = awr_state = 0;
778 ft->xblk = ft->curseg->reqblk;
779 ft->xcnt = 0;
780 ft->xptr = ft->curseg->buff;
781 DPRT(("I/O reqblk = %d\n", ft->curseg->reqblk));
782 goto restate;
783 }
784 if (lastreq == FTIO_READING) {
785 ft->curseg->reqtype = FTIO_RDAHEAD;
786 ft->curseg->reqblk = ft->xblk;
787 ft->curseg->reqcrc = 0;
788 ft->curseg->reqcan = 0;
789 bzero(ft->curseg->buff, QCV_SEGSIZE);
790 arq_state = ard_state = awr_state = 0;
791 ft->xblk = ft->curseg->reqblk;
792 ft->xcnt = 0;
793 ft->xptr = ft->curseg->buff;
794 DPRT(("Processing readahead reqblk = %d\n", ft->curseg->reqblk));
795 goto restate;
796 }
797 if (ft->moving) {
798 DPRT(("No more I/O.. Stopping.\n"));
799 ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0);
800 break;
801 }
802 arq_state = 7;
803 goto restate;
804
805 case 2: /* End of track */
806 ft->moving = 0;
807 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
808 break;
809
810 case 3:
811 DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk));
812 ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0);
813 break;
814
815 case 4:
816 cmd = (ft->xblk / ftg->g_blktrk) + 2;
817 if (domore) {
818 ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
819 } else {
820 ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0);
821 }
822 break;
823
824 case 5:
825 ft->moving = 1;
826 ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
827 break;
828
829 case 6:
830 arq_state = 1;
831 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
832 break;
833
834 case 7:
835 ft->moving = 0;
836
837 /* Check one last time to see if a request came in. */
838 if (ft->curseg->reqtype != FTIO_READY) {
839 DPRT(("async_req: Never say no!\n"));
840 arq_state = 1;
841 goto restate;
842 }
843
844 /* Time to rest. */
845 ft->active = 0;
846 wakeup((caddr_t)&ftsem.iosts_change); /* wakeup those who want an i/o chg */
847 break;
848 }
849}
850
851/*
852 * Entry for async read.
853 */
854void async_read(ftu_t ftu, int from)
855{
856 ft_p ft = &ft_data[ftu];
857 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
858 unsigned long paddr;
859 int i, cmd, newcn, rddta[7];
860 int st0, pcn, where;
861 static int over_async;
862
863 if (from == 2) ard_state = over_async;
864
865restate:
866#if FTDBGALL
867 DPRT(("async_read: state: %d from = %d\n", ard_state, from));
868#endif
869 switch (ard_state) {
870 case 0: /* Start off */
871 /* If tape is not at desired position, stop and locate */
872 if (ft->lastpos != (ft->xblk-1)) {
873 DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
874 ftu, ft->lastpos, ft->xblk));
875 ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0);
876 }
877
878 /* Tape is in position but stopped. */
879 if (!ft->moving) {
880 DPRT(("async_read ******STARTING TAPE\n"));
881 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
882 }
883 ard_state = 1;
884 goto restate;
885
886 case 1: /* Start DMA */
887 /* Tape is now moving and in position-- start DMA now! */
888 isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2);
889 out_fdc(fdcu, 0x66); /* read */
890 out_fdc(fdcu, 0x00); /* unit */
891 out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
892 out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
893 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
894 out_fdc(fdcu, 0x03); /* 1K sectors */
895 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
896 out_fdc(fdcu, 0x74); /* gap length */
897 out_fdc(fdcu, 0xff); /* transfer size */
898 ard_state = 2;
899 break;
900
901 case 2: /* DMA completed */
902 /* Transfer complete, get status */
903 for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
904 isa_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2);
905
906#if FTDBGALL
907 /* Compute where the controller thinks we are */
908 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
909 DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
910 rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
911 where, ft->xblk));
912#endif
913
914 /* Check for errors */
4af7e30c 915 if ((rddta[0] & 0xc0) != 0x00) {
df30aeb2
AM
916 if (rddta[1] & 0x04) {
917 /* Probably wrong position */
918 ft->lastpos = ft->xblk;
919 ard_state = 0;
920 goto restate;
921 } else {
922 /* CRC/Address-mark/Data-mark, et. al. */
923 DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk));
924 ft->curseg->reqcrc |= (1 << ft->xcnt);
925 }
926 }
927
928 /* Otherwise, transfer completed okay. */
929 ft->lastpos = ft->xblk;
930 ft->xblk++;
931 ft->xcnt++;
932 ft->xptr += QCV_BLKSIZE;
933 if (ft->xcnt < QCV_BLKSEG && ft->curseg->reqcan == 0) {
934 ard_state = 0;
935 goto restate;
936 }
937 DPRT(("Read done.. Cancel = %d\n", ft->curseg->reqcan));
938 ft->io_sts = FTIO_READY;
939 break;
940
941 case 3:
942 ft->moving = 1;
943 ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
944 break;
945
946 case 4:
947 ard_state = 1;
948 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
949 break;
950
951 default:
952 DPRT(("ft%d: bad async_read state %d!!\n", ftu, ard_state));
953 break;
954 }
955}
956
957
958/*
959 * Entry for async write. If from is 0, this came from the interrupt
960 * routine, if it's 1 then it was a timeout, if it's 2, then an
961 * async_cmd completed.
962 */
963void async_write(ftu_t ftu, int from)
964{
965 ft_p ft = &ft_data[ftu];
966 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
967 unsigned long paddr;
968 int i, cmd, newcn, rddta[7];
969 int st0, pcn, where;
970 static int over_async;
971 static int retries = 0;
972
973 if (from == 2) awr_state = over_async;
974
975restate:
976#if FTDBGALL
977 DPRT(("async_write: state: %d from = %d\n", awr_state, from));
978#endif
979 switch (awr_state) {
980 case 0: /* Start off */
981 /* If tape is not at desired position, stop and locate */
982 if (ft->lastpos != (ft->xblk-1)) {
983 DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
984 ftu, ft->lastpos, ft->xblk));
985 ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0);
986 }
987
988 /* Tape is in position but stopped. */
989 if (!ft->moving) {
990 DPRT(("async_write ******STARTING TAPE\n"));
991 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0);
992 }
993 awr_state = 1;
994 goto restate;
995
996 case 1: /* Start DMA */
997 /* Tape is now moving and in position-- start DMA now! */
998 isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
999 out_fdc(fdcu, 0x45); /* write */
1000 out_fdc(fdcu, 0x00); /* unit */
1001 out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
1002 out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
1003 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
1004 out_fdc(fdcu, 0x03); /* 1K sectors */
1005 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
1006 out_fdc(fdcu, 0x74); /* gap length */
1007 out_fdc(fdcu, 0xff); /* transfer size */
1008 awr_state = 2;
1009 break;
1010
1011 case 2: /* DMA completed */
1012 /* Transfer complete, get status */
1013 for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
1014 isa_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
1015
1016#if FTDBGALL
1017 /* Compute where the controller thinks we are */
1018 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
1019 DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
1020 rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
1021 where, ft->xblk));
1022#endif
1023
1024 /* Check for errors */
4af7e30c 1025 if ((rddta[0] & 0xc0) != 0x00) {
df30aeb2
AM
1026 if (rddta[1] & 0x04) {
1027 /* Probably wrong position */
1028 ft->lastpos = ft->xblk;
4af7e30c 1029 awr_state = 0;
df30aeb2
AM
1030 goto restate;
1031 } else if (retries < 5) {
1032 /* Something happened -- try again */
1033 ft->lastpos = ft->xblk;
4af7e30c 1034 awr_state = 0;
df30aeb2
AM
1035 retries++;
1036 goto restate;
1037 } else {
1038 /*
1039 * Retries failed. Note the unrecoverable error.
1040 * Marking the block as bad is fairly useless.
1041 */
1042 printf("ft%d: unrecoverable write error on block %d\n",
1043 ftu, ft->xblk);
1044 ft->curseg->reqcrc |= (1 << ft->xcnt);
1045 }
1046 }
1047
1048 /* Otherwise, transfer completed okay. */
1049 retries = 0;
1050 ft->lastpos = ft->xblk;
1051 ft->xblk++;
1052 ft->xcnt++;
1053 ft->xptr += QCV_BLKSIZE;
1054 if (ft->xcnt < QCV_BLKSEG) {
1055 awr_state = 0; /* next block */
1056 goto restate;
1057 }
1058#if FTDBGALL
1059 DPRT(("Write done.\n"));
1060#endif
1061 ft->io_sts = FTIO_READY;
1062 break;
1063
1064 case 3:
1065 ft->moving = 1;
1066 ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0);
1067 break;
1068
1069 case 4:
1070 awr_state = 1;
1071 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
1072 break;
1073
1074 default:
1075 DPRT(("ft%d: bad async_write state %d!!\n", ftu, awr_state));
1076 break;
1077 }
1078}
1079
1080
1081/*
1082 * Interrupt handler for active tape. Bounced off of fdintr().
1083 */
1084int ftintr(ftu_t ftu)
1085{
1086 int st0, pcn, i;
1087 ft_p ft = &ft_data[ftu];
1088 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1089 st0 = 0;
1090 pcn = 0;
1091
1092 /* I/O segment transfer completed */
1093 if (ft->active) {
1094 if (async_func != ACMD_NONE) {
1095 async_cmd(ftu);
1096 return(1);
1097 }
1098#if FTDBGALL
1099 DPRT(("Got request interrupt\n"));
1100#endif
1101 async_req(ftu, 0);
1102 return(1);
1103 }
1104
1105 /* Get interrupt status */
1106 if (ft->cmd_wait != FTCMD_READID) {
1107 out_fdc(fdcu, NE7CMD_SENSEI);
1108 st0 = in_fdc(fdcu);
1109 pcn = in_fdc(fdcu);
1110 }
1111
1112 if (ft->cmd_wait == FTCMD_NONE || ft->sts_wait != FTSTS_SNOOZE) {
1113huh_what:
1114 printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n",
1115 ftu, st0, pcn);
1116 return(1);
1117 }
1118
1119 switch (ft->cmd_wait) {
1120 case FTCMD_RESET:
1121 ft->sts_wait = FTSTS_INTERRUPT;
1122 wakeup((caddr_t)&ftsem.intr_wait);
1123 break;
1124 case FTCMD_RECAL:
1125 case FTCMD_SEEK:
1126 if (st0 & 0x20) { /* seek done */
1127 ft->sts_wait = FTSTS_INTERRUPT;
1128 ft->pcn = pcn;
1129 wakeup((caddr_t)&ftsem.intr_wait);
1130 }
1131#if FTDBGALL
1132 else
1133 DPRT(("ft%d: seek error st0 = $%02x pcn = %d\n",
1134 ftu, st0, pcn));
1135#endif
1136 break;
1137 case FTCMD_READID:
1138 for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
1139 ft->sts_wait = FTSTS_INTERRUPT;
1140 wakeup((caddr_t)&ftsem.intr_wait);
1141 break;
1142
1143 default:
1144 goto huh_what;
1145 }
1146
1147 return(1);
1148}
1149
1150/*
1151 * Interrupt timeout routine.
1152 */
1153static void ft_timeout(caddr_t arg1, int arg2)
1154{
1155 int s;
1156 ftu_t ftu = (ftu_t)arg1;
1157 ft_p ft = &ft_data[ftu];
1158
1159 s = splbio();
1160 if (ft->active) {
1161 if (async_func != ACMD_NONE) {
1162 async_cmd(ftu);
1163 splx(s);
1164 return;
1165 }
1166 async_req(ftu, 1);
1167 } else {
1168 ft->sts_wait = FTSTS_TIMEOUT;
1169 wakeup((caddr_t)&ftsem.intr_wait);
1170 }
1171 splx(s);
1172}
1173
1174/*
1175 * Wait for a particular interrupt to occur. ftintr() will wake us up
1176 * if it sees what we want. Otherwise, time out and return error.
1177 * Should always disable ints before trigger is sent and calling here.
1178 */
1179int ftintr_wait(ftu_t ftu, int cmd, int ticks)
1180{
1181 int retries, st0, pcn;
1182 ft_p ft = &ft_data[ftu];
1183 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1184
1185 ft->cmd_wait = cmd;
1186 ft->sts_wait = FTSTS_SNOOZE;
1187
1188 /* At attach time, we can't rely on having interrupts serviced */
1189 if (ft->attaching) {
1190 switch (cmd) {
1191 case FTCMD_RESET:
1192 DELAY(100);
1193 ft->sts_wait = FTSTS_INTERRUPT;
1194 goto intrdone;
1195 case FTCMD_RECAL:
1196 case FTCMD_SEEK:
1197 for (retries = 0; retries < 10000; retries++) {
1198 out_fdc(fdcu, NE7CMD_SENSEI);
1199 st0 = in_fdc(fdcu);
1200 pcn = in_fdc(fdcu);
1201 if (st0 & 0x20) {
1202 ft->sts_wait = FTSTS_INTERRUPT;
1203 ft->pcn = pcn;
1204 goto intrdone;
1205 }
1206 DELAY(100);
1207 }
1208 break;
1209 }
1210 ft->sts_wait = FTSTS_TIMEOUT;
1211 goto intrdone;
1212 }
1213
1214 if (ticks) timeout(ft_timeout, (caddr_t)ftu, ticks);
ee874879 1215 tsleep((caddr_t)&ftsem.intr_wait, FTPRI, "ftwait", 0);
df30aeb2
AM
1216
1217intrdone:
1218 if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */
1219 if (ft->cmd_wait != FTCMD_RESET)
1220 DPRT(("ft%d: timeout on command %d\n", ftu, ft->cmd_wait));
1221 ft->cmd_wait = FTCMD_NONE;
1222 ft->sts_wait = FTSTS_NONE;
1223 return(1);
1224 }
1225
1226 /* got interrupt */
1227 if (ft->attaching == 0 && ticks) untimeout(ft_timeout, (caddr_t)ftu);
1228 ft->cmd_wait = FTCMD_NONE;
1229 ft->sts_wait = FTSTS_NONE;
1230 return(0);
1231}
1232
1233/*
1234 * Recalibrate tape drive. Parameter totape is true, if we should
1235 * recalibrate to tape drive settings.
1236 */
1237int tape_recal(ftu_t ftu, int totape)
1238{
1239 int s;
1240 ft_p ft = &ft_data[ftu];
1241 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1242
1243 DPRT(("tape_recal start\n"));
1244
1245 out_fdc(fdcu, NE7CMD_SPECIFY);
1246 out_fdc(fdcu, (totape) ? 0xAD : 0xDF);
1247 out_fdc(fdcu, 0x02);
1248
1249 s = splbio();
1250 out_fdc(fdcu, NE7CMD_RECAL);
1251 out_fdc(fdcu, 0x00);
1252
1253 if (ftintr_wait(ftu, FTCMD_RECAL, hz)) {
1254 splx(s);
1255 DPRT(("ft%d: recalibrate timeout\n", ftu));
1256 return(1);
1257 }
1258 splx(s);
1259
1260 out_fdc(fdcu, NE7CMD_SPECIFY);
1261 out_fdc(fdcu, (totape) ? 0xFD : 0xDF);
1262 out_fdc(fdcu, 0x02);
1263
1264 DPRT(("tape_recal end\n"));
1265 return(0);
1266}
1267
1268static void state_timeout(caddr_t arg1, int arg2)
1269{
1270 ftu_t ftu = (ftu_t)arg1;
1271
1272 wakeup((caddr_t)&ftsem.long_delay);
1273}
1274
1275/*
1276 * Wait for a particular tape status to be met. If all is TRUE, then
1277 * all states must be met, otherwise any state can be met.
1278 */
1279int tape_state(ftu_t ftu, int all, int mask, int seconds)
1280{
1281 int r, tries, maxtries;
1282
1283 maxtries = (seconds) ? (4 * seconds) : 1;
1284 for (tries = 0; tries < maxtries; tries++) {
1285 r = tape_status(ftu);
1286 if (r >= 0) {
1287 if (all && (r & mask) == mask) return(r);
1288 if ((r & mask) != 0) return(r);
1289 }
1290 if (seconds) {
1291 timeout(state_timeout, (caddr_t)ftu, hz/4);
ee874879 1292 tsleep((caddr_t)&ftsem.long_delay, FTPRI, "ftstate", 0);
df30aeb2
AM
1293 }
1294 }
1295 DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n",
1296 ftu, mask, maxtries));
1297 return(-1);
1298}
1299
1300/*
1301 * Send a QIC command to tape drive, wait for completion.
1302 */
1303int tape_cmd(ftu_t ftu, int cmd)
1304{
1305 int newcn;
1306 int retries = 0;
1307 int s;
1308 ft_p ft = &ft_data[ftu];
1309 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1310
1311 DPRT(("===> tape_cmd: %d\n",cmd));
1312 newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
1313
1314retry:
1315
1316 /* Perform seek */
1317 s = splbio();
1318 out_fdc(fdcu, NE7CMD_SEEK);
1319 out_fdc(fdcu, 0x00);
1320 out_fdc(fdcu, newcn);
1321
1322 if (ftintr_wait(ftu, FTCMD_SEEK, hz)) {
1323 DPRT(("ft%d: tape_cmd seek timeout\n", ftu));
1324redo:
1325 splx(s);
1326 if (++retries < 5) goto retry;
1327 DPRT(("ft%d: tape_cmd seek failed!\n", ftu));
1328 return(1);
1329 }
1330 splx(s);
1331
1332 if (ft->pcn != newcn) {
1333 DPRT(("ft%d: bad seek in tape_cmd; pcn = %d newcn = %d\n",
1334 ftu, ft->pcn, newcn));
1335 goto redo;
1336 }
1337 DELAY(2500);
1338 return(0);
1339}
1340
1341/*
1342 * Return status of tape drive
1343 */
1344int tape_status(ftu_t ftu)
1345{
1346 int r, err, tries;
1347 ft_p ft = &ft_data[ftu];
1348
1349 for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
1350 r = qic_status(ftu, QC_STATUS, 8);
1351 if (tries == 3) return(-1);
1352 DPRT(("tape_status got $%04x\n",r));
1353 ft->laststs = r;
1354
1355 if (r & (QS_ERROR|QS_NEWCART)) {
1356 if (r & QS_NEWCART) ft->newcart = 1;
1357 err = qic_status(ftu, QC_ERRCODE, 16);
1358 ft->lasterr = err;
1359 if ((r & QS_NEWCART) == 0 && err && ft->attaching == 0) {
1360 DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
1361 ftu, err & 0xff, err >> 8));
1362 }
1363 r = qic_status(ftu, QC_STATUS, 8);
1364 ft->laststs = r;
1365 DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r));
1366 }
1367 ft->rdonly = (r & QS_RDONLY);
1368 return(r);
1369}
1370
1371/*
1372 * Transfer control to tape drive.
1373 */
1374void tape_start(ftu_t ftu)
1375{
1376 ft_p ft = &ft_data[ftu];
1377 fdc_p fdc = ft->fdc;
1378 int s;
1379
1380 DPRT(("tape_start start\n"));
1381
1382 s = splbio();
1383
1384 /* reset, dma disable */
1385 outb(fdc->baseport+fdout, 0x00);
1386 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1387
1388 /* raise reset, enable DMA */
1389 outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN);
1390 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1391
1392 splx(s);
1393
1394 tape_recal(ftu, 1);
1395
1396 /* set transfer speed */
1397 outb(fdc->baseport+fdctl, FDC_500KBPS);
1398 DELAY(10);
1399
1400 DPRT(("tape_start end\n"));
1401}
1402
1403/*
1404 * Transfer control back to floppy disks.
1405 */
1406void tape_end(ftu_t ftu)
1407{
1408 ft_p ft = &ft_data[ftu];
1409 fdc_p fdc = ft->fdc;
1410 int s;
1411
1412 DPRT(("tape_end start\n"));
1413 tape_recal(ftu, 0);
1414
1415 s = splbio();
1416
1417 /* reset, dma disable */
1418 outb(fdc->baseport+fdout, 0x00);
1419 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1420
1421 /* raise reset, enable DMA */
1422 outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN);
1423 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1424
1425 splx(s);
1426
1427 /* set transfer speed */
1428 outb(fdc->baseport+fdctl, FDC_500KBPS);
1429 DELAY(10);
1430 fdc->flags &= ~FDC_TAPE_BUSY;
1431
1432 DPRT(("tape_end end\n"));
1433}
1434
1435/*
1436 * Wait for the driver to go inactive, cancel readahead if necessary.
1437 */
1438void tape_inactive(ftu_t ftu)
1439{
1440 ft_p ft = &ft_data[ftu];
1441
1442 if (ft->curseg->reqtype == FTIO_RDAHEAD) {
1443 ft->curseg->reqcan = 1; /* XXX cancel rdahead */
ee874879
GW
1444 while (ft->active)
1445 tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftinact", 0);
df30aeb2 1446 }
ee874879
GW
1447 while (ft->active)
1448 tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftinact", 0);
df30aeb2
AM
1449}
1450
1451/*
1452 * Get the geometry of the tape currently in the drive.
1453 */
1454int ftgetgeom(ftu_t ftu)
1455{
1456 int r, i, tries;
1457 int cfg, qic80, ext;
1458 int sts, fmt, len;
1459 ft_p ft = &ft_data[ftu];
1460
1461 r = tape_status(ftu);
1462
1463 /* XXX fix me when format mode is finished */
1464 if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) {
1465 DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r));
1466 ftg = NULL;
1467 ft->newcart = 1;
1468 return(0);
1469 }
1470
1471 /* Report drive configuration */
1472 for (cfg = -1, tries = 0; cfg < 0 && tries < 3; tries++)
1473 cfg = qic_status(ftu, QC_CONFIG, 8);
1474 if (tries == 3) {
1475 DPRT(("ftgetgeom report config failed\n"));
1476 ftg = NULL;
1477 return(-1);
1478 }
1479 DPRT(("ftgetgeom report config got $%04x\n", cfg));
1480 ft->lastcfg = cfg;
1481
1482 qic80 = cfg & QCF_QIC80;
1483 ext = cfg & QCF_EXTRA;
1484
1485/*
1486 * XXX - This doesn't seem to work on my Colorado Jumbo 250...
1487 * if it works on your drive, I'd sure like to hear about it.
1488 */
1489#if 0
1490 /* Report drive status */
1491 for (sts = -1, tries = 0; sts < 0 && tries < 3; tries++)
1492 sts = qic_status(ftu, QC_TSTATUS, 8);
1493 if (tries == 3) {
1494 DPRT(("ftgetgeom report tape status failed\n"));
1495 ftg = NULL;
1496 return(-1);
1497 }
1498 DPRT(("ftgetgeom report tape status got $%04x\n", sts));
1499#else
1500 /*
1501 * XXX - Forge a fake tape status based upon the returned
1502 * configuration, since the above command or code is broken
1503 * for my drive and probably other older drives.
1504 */
1505 sts = 0;
1506 sts = (qic80) ? QTS_QIC80 : QTS_QIC40;
1507 sts |= (ext) ? QTS_LEN2 : QTS_LEN1;
1508#endif
1509
1510 fmt = sts & QTS_FMMASK;
1511 len = (sts & QTS_LNMASK) >> 4;
1512
1513 if (fmt > QCV_NFMT) {
1514 ftg = NULL;
1515 printf("ft%d: unsupported tape format\n", ftu);
1516 return(-1);
1517 }
1518 if (len > QCV_NLEN) {
1519 ftg = NULL;
1520 printf("ft%d: unsupported tape length\n", ftu);
1521 return(-1);
1522 }
1523
1524 /* Look up geometry in the table */
1525 for (i = 1; i < NGEOM; i++)
1526 if (ftgtbl[i].g_fmtno == fmt && ftgtbl[i].g_lenno == len) break;
1527 if (i == NGEOM) {
1528 printf("ft%d: unknown tape geometry\n", ftu);
1529 ftg = NULL;
1530 return(-1);
1531 }
1532 ftg = &ftgtbl[i];
1533 if (!ftg->g_trktape) {
1534 printf("ft%d: unsupported format %s w/len %s\n",
1535 ftu, ftg->g_fmtdesc, ftg->g_lendesc);
1536 ftg = NULL;
1537 return(-1);
1538 }
1539 DPRT(("Tape format is %s, length is %s\n", ftg->g_fmtdesc, ftg->g_lendesc));
1540 ft->newcart = 0;
1541 return(0);
1542}
1543
1544/*
1545 * Switch between tape/floppy. This will send the tape enable/disable
1546 * codes for this drive's manufacturer.
1547 */
1548int set_fdcmode(dev_t dev, int newmode)
1549{
1550 ftu_t ftu = FDUNIT(minor(dev));
1551 ft_p ft = &ft_data[ftu];
1552 fdc_p fdc = ft->fdc;
1553
1554 static int havebufs = 0;
1555 void *buf;
1556 int r, s, i;
1557 SegReq *sp;
1558
1559 if (newmode == FDC_TAPE_MODE) {
1560 /* Wake up the tape drive */
1561 switch (ft->type) {
1562 case NO_TYPE:
1563 fdc->flags &= ~FDC_TAPE_BUSY;
1564 return(ENXIO);
1565 case FT_COLORADO:
1566 tape_start(ftu);
1567 if (tape_cmd(ftu, QC_COL_ENABLE1)) {
1568 tape_end(ftu);
1569 return(EIO);
1570 }
1571 if (tape_cmd(ftu, QC_COL_ENABLE2)) {
1572 tape_end(ftu);
1573 return(EIO);
1574 }
1575 break;
1576 case FT_MOUNTAIN:
1577 tape_start(ftu);
1578 if (tape_cmd(ftu, QC_MTN_ENABLE1)) {
1579 tape_end(ftu);
1580 return(EIO);
1581 }
1582 if (tape_cmd(ftu, QC_MTN_ENABLE2)) {
1583 tape_end(ftu);
1584 return(EIO);
1585 }
1586 break;
1587 default:
1588 DPRT(("ft%d: bad tape type\n", ftu));
1589 return(ENXIO);
1590 }
1591 if (tape_status(ftu) < 0) {
1592 tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
1593 tape_end(ftu);
1594 return(EIO);
1595 }
1596
1597 /* Grab buffers from memory. */
1598 if (!havebufs) {
1599 ft->curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT);
1600 if (ft->curseg == NULL) {
1601 printf("ft%d: not enough memory for buffers\n", ftu);
1602 return(ENOMEM);
1603 }
1604 ft->bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT);
1605 if (ft->bufseg == NULL) {
1606 free(ft->curseg, M_DEVBUF);
1607 printf("ft%d: not enough memory for buffers\n", ftu);
1608 return(ENOMEM);
1609 }
1610 havebufs = 1;
1611 }
1612 ft->curseg->reqtype = FTIO_READY;
1613 ft->bufseg->reqtype = FTIO_READY;
1614 ft->io_sts = FTIO_READY; /* tape drive is ready */
1615 ft->active = 0; /* interrupt driver not active */
1616 ft->moving = 0; /* tape not moving */
1617 ft->rdonly = 0; /* tape read only */
1618 ft->newcart = 0; /* a new cart was inserted */
1619 ft->lastpos = -1; /* tape is rewound */
1620 tape_state(ftu, 0, QS_READY, 60);
1621 tape_cmd(ftu, QC_RATE);
1622 tape_cmd(ftu, QCF_RT500+2); /* 500K bps */
1623 tape_state(ftu, 0, QS_READY, 60);
1624 ft->mode = FTM_PRIMARY;
1625 tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */
1626 tape_state(ftu, 0, QS_READY, 60);
1627 ftg = NULL; /* No geometry yet */
1628 ftgetgeom(ftu); /* Get tape geometry */
1629 ftreq_rewind(ftu); /* Make sure tape is rewound */
1630 } else {
1631 tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE);
1632 tape_end(ftu);
1633 ft->newcart = 0; /* clear new cartridge */
1634 havebufs = 0;
1635 free(ft->curseg, M_DEVBUF);
1636 free(ft->bufseg, M_DEVBUF);
1637 }
1638 return(0);
1639}
1640
1641
1642/*
1643 * Perform a QIC status function.
1644 */
1645int qic_status(ftu_t ftu, int cmd, int nbits)
1646{
1647 int st3, val, r, i;
1648 ft_p ft = &ft_data[ftu];
1649 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1650
1651 if (tape_cmd(ftu, cmd)) {
1652 DPRT(("ft%d: QIC status timeout\n", ftu));
1653 return(-1);
1654 }
1655
1656 /* Sense drive status */
1657 out_fdc(fdcu, NE7CMD_SENSED);
1658 out_fdc(fdcu, 0x00);
1659 st3 = in_fdc(fdcu);
1660
1661 if ((st3 & 0x10) == 0) { /* track 0 */
1662 DPRT(("qic_status has dead drive... st3 = $%02x\n", st3));
1663 return(-1);
1664 }
1665
1666 for (i = r = 0; i <= nbits; i++) {
1667 if (tape_cmd(ftu, QC_NEXTBIT)) {
1668 DPRT(("ft%d: QIC status bit timed out on %d\n", ftu, i));
1669 return(-1);
1670 }
1671
1672 out_fdc(fdcu, NE7CMD_SENSED);
1673 out_fdc(fdcu, 0x00);
1674 st3 = in_fdc(fdcu);
1675 if (st3 < 0) {
1676 DPRT(("ft%d: controller timed out on bit %d r=$%02x\n",
1677 ftu, i, r));
1678 return(-1);
1679 }
1680
1681 r >>= 1;
1682 if (i < nbits)
1683 r |= ((st3 & 0x10) ? 1 : 0) << nbits;
1684 else if ((st3 & 0x10) == 0) {
1685 DPRT(("ft%d: qic status stop bit missing at %d, st3=$%02x r=$%04x\n",
1686 ftu,i,st3,r));
1687 return(-1);
1688 }
1689 }
1690
1691 DPRT(("qic_status returned $%02x\n", r));
1692 return(r);
1693}
1694
1695/*
1696 * Open tape drive for use. Bounced off of Fdopen if tape minor is
1697 * detected.
1698 */
1699int ftopen(dev_t dev, int arg2) {
1700 ftu_t ftu = FDUNIT(minor(dev));
1701 int type = FDTYPE(minor(dev));
4af7e30c 1702 fdc_p fdc;
df30aeb2 1703
4af7e30c
NW
1704 /* check bounds */
1705 if (ftu >= NFT)
1706 return(ENXIO);
1707 fdc = ft_data[ftu].fdc;
df30aeb2
AM
1708 /* check for controller already busy with tape */
1709 if (fdc->flags & FDC_TAPE_BUSY)
1710 return(EBUSY);
1711 /* make sure we found a tape when probed */
1712 if (!(fdc->flags & FDC_HASFTAPE))
1713 return(ENODEV);
56446ddd 1714 fdc->fdu = ftu;
df30aeb2
AM
1715 fdc->flags |= FDC_TAPE_BUSY;
1716 return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */
1717}
1718
1719/*
1720 * Close tape and return floppy controller to disk mode.
1721 */
1722int ftclose(dev_t dev, int flags)
1723{
1724 int s;
1725 SegReq *sp;
1726 ftu_t ftu = FDUNIT(minor(dev));
1727 ft_p ft = &ft_data[ftu];
1728
1729 /* Wait for any remaining I/O activity to complete. */
1730 if (ft->curseg->reqtype == FTIO_RDAHEAD) ft->curseg->reqcan = 1;
ee874879
GW
1731 while (ft->active)
1732 tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftclose", 0);
df30aeb2
AM
1733
1734 ft->mode = FTM_PRIMARY;
1735 tape_cmd(ftu, QC_PRIMARY);
1736 tape_state(ftu, 0, QS_READY, 60);
1737 ftreq_rewind(ftu);
1738 return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */
1739}
1740
1741/*
1742 * Perform strategy on a given buffer (not!). The driver was not
1743 * performing very efficiently using the buffering routines. After
1744 * support for error correction was added, this routine became
1745 * obsolete in favor of doing ioctl's. Ugly, yes.
1746 */
1747void ftstrategy(struct buf *bp)
1748{
1749 return;
1750}
1751
1752/* Read or write a segment. */
1753int ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p)
1754{
1755 int r, i, j;
1756 SegReq *sp;
1757 int s;
1758 long blk, bad;
1759 unsigned char *cp, *cp2;
1760 ft_p ft = &ft_data[ftu];
1761
1762 if (!ft->active) {
1763 r = tape_status(ftu);
1764 if ((r & QS_CART) == 0) {
1765 return(ENXIO); /* No cartridge */
1766 }
1767 if ((r & QS_FMTOK) == 0) {
1768 return(ENXIO); /* Not formatted */
1769 }
1770 tape_state(ftu, 0, QS_READY, 90);
1771 }
1772
1773 if (ftg == NULL || ft->newcart) {
ee874879
GW
1774 while (ft->active)
1775 tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0);
df30aeb2
AM
1776 tape_state(ftu, 0, QS_READY, 90);
1777 if (ftgetgeom(ftu) < 0) {
1778 return(ENXIO);
1779 }
1780 }
1781
1782 /* Write not allowed on a read-only tape. */
1783 if (cmd == QIOWRITE && ft->rdonly) {
1784 return(EROFS);
1785 }
1786 /* Quick check of request and buffer. */
1787 if (sr == NULL || sr->sg_data == NULL) {
1788 return(EINVAL);
1789 }
1790 if (sr->sg_trk >= ftg->g_trktape ||
1791 sr->sg_seg >= ftg->g_segtrk) {
1792 return(EINVAL);
1793 }
1794 blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
1795
1796 s = splbio();
1797 if (cmd == QIOREAD) {
1798 if (ft->curseg->reqtype == FTIO_RDAHEAD) {
1799 if (blk == ft->curseg->reqblk) {
1800 sp = ft->curseg;
1801 sp->reqtype = FTIO_READING;
1802 sp->reqbad = sr->sg_badmap;
1803 goto rdwait;
1804 } else
1805 ft->curseg->reqcan = 1; /* XXX cancel rdahead */
1806 }
1807
1808 /* Wait until we're ready. */
ee874879
GW
1809 while (ft->active)
1810 tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0);
df30aeb2
AM
1811
1812 /* Set up a new read request. */
1813 sp = ft->curseg;
1814 sp->reqcrc = 0;
1815 sp->reqbad = sr->sg_badmap;
1816 sp->reqblk = blk;
1817 sp->reqcan = 0;
1818 sp->reqtype = FTIO_READING;
1819
1820 /* Start the read request off. */
1821 DPRT(("Starting read I/O chain\n"));
1822 arq_state = ard_state = awr_state = 0;
1823 ft->xblk = sp->reqblk;
1824 ft->xcnt = 0;
1825 ft->xptr = sp->buff;
1826 ft->active = 1;
1827 timeout(ft_timeout, (caddr_t)ftu, 1);
1828
1829rdwait:
ee874879 1830 tsleep((caddr_t)&ftsem.buff_avail, FTPRI, "ftrw", 0);
df30aeb2
AM
1831 bad = sp->reqbad;
1832 sr->sg_crcmap = sp->reqcrc & ~bad;
1833
1834 /* Copy out segment and discard bad mapped blocks. */
1835 cp = sp->buff; cp2 = sr->sg_data;
1836 for (i = 0; i < QCV_BLKSEG; cp += QCV_BLKSIZE, i++) {
1837 if (bad & (1 << i)) continue;
1838 copyout(cp, cp2, QCV_BLKSIZE);
1839 cp2 += QCV_BLKSIZE;
1840 }
1841 } else {
1842 if (ft->curseg->reqtype == FTIO_RDAHEAD) {
1843 ft->curseg->reqcan = 1; /* XXX cancel rdahead */
1844 while (ft->active)
ee874879 1845 tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0);
df30aeb2
AM
1846 }
1847
1848 /* Sleep until a buffer becomes available. */
1849 while (ft->bufseg->reqtype != FTIO_READY)
ee874879 1850 tsleep((caddr_t)&ftsem.buff_avail, FTPRI, "ftrwbuf", 0);
df30aeb2
AM
1851 sp = (ft->curseg->reqtype == FTIO_READY) ? ft->curseg : ft->bufseg;
1852
1853 /* Copy in segment and expand bad blocks. */
1854 bad = sr->sg_badmap;
1855 cp = sr->sg_data; cp2 = sp->buff;
1856 for (i = 0; i < QCV_BLKSEG; cp2 += QCV_BLKSIZE, i++) {
1857 if (bad & (1 << i)) continue;
1858 copyin(cp, cp2, QCV_BLKSIZE);
1859 cp += QCV_BLKSIZE;
1860 }
1861
1862 sp->reqblk = blk;
1863 sp->reqcan = 0;
1864 sp->reqtype = FTIO_WRITING;
1865
1866 if (!ft->active) {
1867 DPRT(("Starting write I/O chain\n"));
1868 arq_state = ard_state = awr_state = 0;
1869 ft->xblk = sp->reqblk;
1870 ft->xcnt = 0;
1871 ft->xptr = sp->buff;
1872 ft->active = 1;
1873 timeout(ft_timeout, (caddr_t)ftu, 1);
1874 }
1875 }
1876 splx(s);
1877 return(0);
1878}
1879
1880
1881/* Rewind to beginning of tape */
1882int ftreq_rewind(ftu_t ftu)
1883{
1884 ft_p ft = &ft_data[ftu];
1885
1886 tape_inactive(ftu);
1887 tape_cmd(ftu, QC_STOP);
1888 tape_state(ftu, 0, QS_READY, 90);
1889 tape_cmd(ftu, QC_SEEKSTART);
1890 tape_state(ftu, 0, QS_READY, 90);
1891 tape_cmd(ftu, QC_SEEKTRACK);
1892 tape_cmd(ftu, 2);
1893 tape_state(ftu, 0, QS_READY, 90);
1894 ft->lastpos = -1;
1895 ft->moving = 0;
1896 return(0);
1897}
1898
1899/* Move to logical beginning or end of track */
1900int ftreq_trkpos(ftu_t ftu, int req)
1901{
1902 int curtrk, r, cmd;
1903 ft_p ft = &ft_data[ftu];
1904
1905 tape_inactive(ftu);
1906 tape_cmd(ftu, QC_STOP);
1907 tape_state(ftu, 0, QS_READY, 90);
1908
1909 r = tape_status(ftu);
1910 if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
1911 if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
1912
1913 if (ftg == NULL || ft->newcart) {
1914 if (ftgetgeom(ftu) < 0) return(ENXIO);
1915 }
1916
1917 curtrk = (ft->lastpos < 0) ? 0 : ft->lastpos / ftg->g_blktrk;
1918 if (req == QIOBOT)
1919 cmd = (curtrk & 1) ? QC_SEEKEND : QC_SEEKSTART;
1920 else
1921 cmd = (curtrk & 1) ? QC_SEEKSTART : QC_SEEKEND;
1922 tape_cmd(ftu, cmd);
1923 tape_state(ftu, 0, QS_READY, 90);
1924 return(0);
1925}
1926
1927/* Seek tape head to a particular track. */
1928int ftreq_trkset(ftu_t ftu, int *trk)
1929{
1930 int curtrk, r, cmd;
1931 ft_p ft = &ft_data[ftu];
1932
1933 tape_inactive(ftu);
1934 tape_cmd(ftu, QC_STOP);
1935 tape_state(ftu, 0, QS_READY, 90);
1936
1937 r = tape_status(ftu);
1938 if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
1939 if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
1940 if (ftg == NULL || ft->newcart) {
1941 if (ftgetgeom(ftu) < 0) return(ENXIO);
1942 }
1943
1944 tape_cmd(ftu, QC_SEEKTRACK);
1945 tape_cmd(ftu, *trk + 2);
1946 tape_state(ftu, 0, QS_READY, 90);
1947 return(0);
1948}
1949
1950/* Start tape moving forward. */
1951int ftreq_lfwd(ftu_t ftu)
1952{
1953 tape_inactive(ftu);
1954 tape_cmd(ftu, QC_STOP);
1955 tape_state(ftu, 0, QS_READY, 90);
1956 tape_cmd(ftu, QC_FORWARD);
1957 return(0);
1958}
1959
1960/* Stop the tape */
1961int ftreq_stop(ftu_t ftu)
1962{
1963 tape_inactive(ftu);
1964 tape_cmd(ftu, QC_STOP);
1965 tape_state(ftu, 0, QS_READY, 90);
1966 return(0);
1967}
1968
1969/* Set the particular mode the drive should be in. */
1970int ftreq_setmode(ftu_t ftu, int cmd)
1971{
1972 int r;
1973 ft_p ft = &ft_data[ftu];
1974
1975 tape_inactive(ftu);
1976 r = tape_status(ftu);
1977
1978 switch(cmd) {
1979 case QIOPRIMARY:
1980 ft->mode = FTM_PRIMARY;
1981 tape_cmd(ftu, QC_PRIMARY);
1982 break;
1983 case QIOFORMAT:
1984 if (r & QS_RDONLY) return(ENXIO);
1985 if ((r & QS_BOT) == 0) return(ENXIO);
1986 tape_cmd(ftu, QC_FORMAT);
1987 break;
1988 case QIOVERIFY:
1989 if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
1990 tape_cmd(ftu, QC_VERIFY);
1991 break;
1992 }
1993 tape_state(ftu, 0, QS_READY, 60);
1994 return(0);
1995}
1996
1997/* Return drive status bits */
1998int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
1999{
2000 ft_p ft = &ft_data[ftu];
2001
2002 if (ft->active)
2003 *sts = ft->laststs & ~QS_READY;
2004 else
2005 *sts = tape_status(ftu);
2006 return(0);
2007}
2008
2009/* Return drive configuration bits */
2010int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p)
2011{
2012 int r, tries;
2013 ft_p ft = &ft_data[ftu];
2014
2015 if (ft->active)
2016 r = ft->lastcfg;
2017 else {
2018 for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
2019 r = qic_status(ftu, QC_CONFIG, 8);
2020 if (tries == 3) return(ENXIO);
2021 }
2022 *cfg = r;
2023 return(0);
2024}
2025
2026/* Return current tape's geometry. */
2027int ftreq_geom(ftu_t ftu, QIC_Geom *g)
2028{
2029 tape_inactive(ftu);
2030 if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO);
2031 bcopy(ftg, g, sizeof(QIC_Geom));
2032 return(0);
2033}
2034
2035/* Return drive hardware information */
2036int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
2037{
2038 int r, tries;
2039 int rom, vend;
2040
2041 tape_inactive(ftu);
2042 bzero(hwp, sizeof(QIC_HWInfo));
2043
2044 for (rom = -1, tries = 0; rom < 0 && tries < 3; tries++)
2045 rom = qic_status(ftu, QC_VERSION, 8);
2046 if (rom > 0) {
2047 hwp->hw_rombeta = (rom >> 7) & 0x01;
2048 hwp->hw_romid = rom & 0x7f;
2049 }
2050
2051 for (vend = -1, tries = 0; vend < 0 && tries < 3; tries++)
2052 vend = qic_status(ftu, QC_VENDORID, 16);
2053 if (vend > 0) {
2054 hwp->hw_make = (vend >> 6) & 0x3ff;
2055 hwp->hw_model = vend & 0x3f;
2056 }
2057
2058 return(0);
2059}
2060
2061/*
2062 * I/O functions.
2063 */
2064int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
2065{
2066 ftu_t ftu = FDUNIT(minor(dev));
2067 ft_p ft = &ft_data[ftu];
2068
2069 switch(cmd) {
2070 case QIOREAD: /* Request reading a segment from tape. */
2071 case QIOWRITE: /* Request writing a segment to tape. */
2072 return(ftreq_rw(ftu, cmd, (QIC_Segment *)data, p));
2073
2074 case QIOREWIND: /* Rewind tape. */
2075 return(ftreq_rewind(ftu));
2076
2077 case QIOBOT: /* Seek to logical beginning of track. */
2078 case QIOEOT: /* Seek to logical end of track. */
2079 return(ftreq_trkpos(ftu, cmd));
2080
2081 case QIOTRACK: /* Seek tape head to specified track. */
2082 return(ftreq_trkset(ftu, (int *)data));
2083
2084 case QIOSEEKLP: /* Seek load point. */
2085 goto badreq;
2086
2087 case QIOFORWARD: /* Move tape in logical forward direction. */
2088 return(ftreq_lfwd(ftu));
2089
2090 case QIOSTOP: /* Causes tape to stop. */
2091 return(ftreq_stop(ftu));
2092
2093 case QIOPRIMARY: /* Enter primary mode. */
2094 case QIOFORMAT: /* Enter format mode. */
2095 case QIOVERIFY: /* Enter verify mode. */
2096 return(ftreq_setmode(ftu, cmd));
2097
2098 case QIOWRREF: /* Write reference burst. */
2099 goto badreq;
2100
2101 case QIOSTATUS: /* Get drive status. */
2102 return(ftreq_status(ftu, cmd, (int *)data, p));
2103
2104 case QIOCONFIG: /* Get tape configuration. */
2105 return(ftreq_config(ftu, cmd, (int *)data, p));
2106
2107 case QIOGEOM:
2108 return(ftreq_geom(ftu, (QIC_Geom *)data));
2109
2110 case QIOHWINFO:
2111 return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data));
2112 }
2113badreq:
2114 DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd));
2115 return(ENXIO);
2116}
2117
2118/* Not implemented */
2119int ftdump(dev_t dev)
2120{
2121 return(EINVAL);
2122}
2123
2124/* Not implemented */
2125int ftsize(dev_t dev)
2126{
2127 return(EINVAL);
2128}
2129#endif