FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/ft.c
1 /*
2 * Copyright (c) 1993, 1994 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
20 * $FreeBSD: src/sys/i386/isa/ft.c,v 1.27.2.4 1999/09/05 08:12:38 peter Exp $
21 *
22 * 01/19/95 ++sg
23 * Cleaned up recalibrate/seek code at attach time for FreeBSD 2.x.
24 *
25 * 06/07/94 v0.9 ++sg
26 * Tape stuck on segment problem should be gone. Re-wrote buffering
27 * scheme. Added support for drives that do not automatically perform
28 * seek load point. Can handle more wakeup types now and should correctly
29 * report most manufacturer names. Fixed places where unit 0 was being
30 * sent to the fdc instead of the actual unit number. Added ioctl support
31 * for an in-core badmap.
32 *
33 * 01/26/94 v0.3b - Jim Babb
34 * Got rid of the hard coded device selection. Moved (some of) the
35 * static variables into a structure for support of multiple devices.
36 * ( still has a way to go for 2 controllers - but closer )
37 * Changed the interface with fd.c so we no longer 'steal' it's
38 * driver routine vectors.
39 *
40 * 10/30/93 v0.3
41 * Fixed a couple more bugs. Reading was sometimes looping when an
42 * an error such as address-mark-missing was encountered. Both
43 * reading and writing was having more backup-and-retries than was
44 * necessary. Added support to get hardware info. Updated for use
45 * with FreeBSD.
46 *
47 * 09/15/93 v0.2 pl01
48 * Fixed a bunch of bugs: extra isa_dmadone() in async_write() (shouldn't
49 * matter), fixed double buffering in async_req(), changed tape_end() in
50 * set_fdcmode() to reduce unexpected interrupts, changed end of track
51 * processing in async_req(), protected more of ftreq_rw() with an
52 * splbio(). Changed some of the ftreq_*() functions so that they wait
53 * for inactivity and then go, instead of aborting immediately.
54 *
55 * 08/07/93 v0.2 release
56 * Shifted from ftstrat to ioctl support for I/O. Streaming is now much
57 * more reliable. Added internal support for error correction, QIC-40,
58 * and variable length tapes. Random access of segments greatly
59 * improved. Formatting and verification support is close but still
60 * incomplete.
61 *
62 * 06/03/93 v0.1 Alpha release
63 * Hopefully the last re-write. Many bugs fixed, many remain.
64 */
65
66 #include "ft.h"
67 #if NFT > 0
68 #include "fd.h"
69
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/conf.h>
73 #include <sys/disklabel.h> /* temp. for dkunit() in fdc.h */
74 #include <sys/file.h>
75 #include <sys/proc.h>
76 #include <sys/ioctl.h>
77 #include <sys/malloc.h>
78 #include <sys/buf.h>
79 #include <sys/uio.h>
80 #include <sys/ftape.h>
81
82 #include <machine/clock.h>
83
84 #include <i386/isa/isa_device.h>
85 #ifdef PC98
86 #include <pc98/pc98/fdreg.h>
87 #else
88 #include <i386/isa/fdreg.h>
89 #include <i386/isa/rtc.h>
90 #endif
91 #include <i386/isa/fdc.h>
92 #include <i386/isa/ftreg.h>
93
94 extern int ftintr __P((ftu_t ftu));
95
96 /* Enable or disable debugging messages. */
97 #define FTDBGALL 0 /* 1 if you want everything */
98 /*#define DPRT(a) printf a */
99 #define DPRT(a)
100
101 /* Constants private to the driver */
102 #define FTPRI (PRIBIO) /* sleep priority */
103 #define FTNBUFF 9 /* 8 for buffering, 1 for header */
104
105 /* The following items are needed from the fd driver. */
106 extern int in_fdc(int); /* read fdc registers */
107 extern int out_fdc(int, int); /* write fdc registers */
108
109 extern int hz; /* system clock rate */
110
111 /* Flags in isadev struct */
112 #define FT_PROBE 0x1 /* allow for "dangerous" tape probes */
113
114 /* Type of tape attached */
115 /* use numbers that don't interfere with the possible floppy types */
116 #define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */
117
118 /* F_TAPE_TYPE must match value in fd.c */
119 #define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */
120 #define FT_NONE (F_TAPE_TYPE | 0) /* no method required */
121 #define FT_MOUNTAIN (F_TAPE_TYPE | 1) /* mountain */
122 #define FT_COLORADO (F_TAPE_TYPE | 2) /* colorado */
123 #define FT_INSIGHT (F_TAPE_TYPE | 3) /* insight */
124
125 /* Mode FDC is currently in: tape or disk */
126 enum { FDC_TAPE_MODE, FDC_DISK_MODE };
127
128 /* Command we are awaiting completion of */
129 enum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID };
130
131 /* Tape interrupt status of current request */
132 enum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT };
133
134 /* Tape I/O status */
135 enum {
136 FTIO_READY, /* No I/O activity */
137 FTIO_READING, /* Currently reading blocks */
138 FTIO_RDAHEAD, /* Currently reading ahead */
139 FTIO_WRITING /* Buffers are being written */
140 };
141
142 /* Current tape mode */
143 enum {
144 FTM_PRIMARY, /* Primary mode */
145 FTM_VERIFY, /* Verify mode */
146 FTM_FORMAT, /* Format mode */
147 FTM_DIAG1, /* Diagnostic mode 1 */
148 FTM_DIAG2 /* Diagnostic mode 2 */
149 };
150
151 /* Tape geometries table */
152 static QIC_Geom ftgtbl[] = {
153 { 0, 0, "Unformatted", "Unknown", 0, 0, 0, 0, 0 }, /* XXX */
154 { 1, 1, "QIC-40", "205/550", 20, 68, 2176, 128, 21760 },
155 { 1, 2, "QIC-40", "307.5/550", 20, 102, 3264, 128, 32640 },
156 { 1, 3, "QIC-40", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
157 { 1, 4, "QIC-40", "1100/550", 20, 365, 11680, 128, 32512 },
158 { 1, 5, "QIC-40", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
159 { 2, 1, "QIC-80", "205/550", 28, 100, 3200, 128, 19200 },
160 { 2, 2, "QIC-80", "307.5/550", 28, 150, 4800, 128, 19200 },
161 { 2, 3, "QIC-80", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
162 { 2, 4, "QIC-80", "1100/550", 28, 537, 17184, 128, 32512 },
163 { 2, 5, "QIC-80", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
164 { 3, 1, "QIC-500", "205/550", 0, 0, 0, 0, 0 }, /* ??? */
165 { 3, 2, "QIC-500", "307.5/550", 0, 0, 0, 0, 0 }, /* ??? */
166 { 3, 3, "QIC-500", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
167 { 3, 4, "QIC-500", "1100/550", 0, 0, 0, 0, 0 }, /* ??? */
168 { 3, 5, "QIC-500", "1100/900", 0, 0, 0, 0, 0 } /* ??? */
169 };
170 #define NGEOM (sizeof(ftgtbl) / sizeof(QIC_Geom))
171
172 static QIC_Geom *ftg = NULL; /* Current tape's geometry */
173
174 /*
175 * things relating to asynchronous commands
176 */
177 static int awr_state; /* state of async write */
178 static int ard_state; /* state of async read */
179 static int arq_state; /* state of async request */
180 static int async_retries; /* retries, one per invocation */
181 static int async_func; /* function to perform */
182 static int async_state; /* state current function is at */
183 static int async_arg0; /* up to 3 arguments for async cmds */
184 static int async_arg1; /**/
185 static int async_arg2; /**/
186 static int async_ret; /* return value */
187 static struct _astk {
188 int over_func;
189 int over_state;
190 int over_retries;
191 int over_arg0;
192 int over_arg1;
193 int over_arg2;
194 } astk[10];
195 static struct _astk *astk_ptr = &astk[0]; /* Pointer to stack position */
196
197 /* List of valid async (interrupt driven) tape support functions. */
198 enum {
199 ACMD_NONE, /* no command */
200 ACMD_SEEK, /* command seek */
201 ACMD_STATUS, /* report status */
202 ACMD_STATE, /* wait for state bits to be true */
203 ACMD_SEEKSTS, /* perform command and wait for status */
204 ACMD_READID, /* read id */
205 ACMD_RUNBLK /* ready tape for I/O on the given block */
206 };
207
208 /* Call another asyncronous command from within async_cmd(). */
209 #define CALL_ACMD(r,f,a,b,c) \
210 astk_ptr->over_retries = async_retries; \
211 astk_ptr->over_func = async_func; \
212 astk_ptr->over_state = (r); \
213 astk_ptr->over_arg0 = async_arg0; \
214 astk_ptr->over_arg1 = async_arg1; \
215 astk_ptr->over_arg2 = async_arg2; \
216 async_func = (f); async_state = 0; async_retries = 0; \
217 async_arg0=(a); async_arg1=(b); async_arg2=(c); \
218 astk_ptr++; \
219 goto restate
220
221 /* Perform an asyncronous command from outside async_cmd(). */
222 #define ACMD_FUNC(r,f,a,b,c) over_async = (r); astk_ptr = &astk[0]; \
223 async_func = (f); async_state = 0; async_retries = 0; \
224 async_arg0=(a); async_arg1=(b); async_arg2=(c); \
225 async_cmd(ftu); \
226 return
227
228 /* Various wait channels */
229 static char *wc_buff_avail = "bavail";
230 static char *wc_buff_done = "bdone";
231 static char *wc_iosts_change = "iochg";
232 static char *wc_long_delay = "ldelay";
233 static char *wc_intr_wait = "intrw";
234 #define ftsleep(wc,to) tsleep((caddr_t)(wc),FTPRI,(wc),(to))
235
236 /***********************************************************************\
237 * Per controller structure. *
238 \***********************************************************************/
239 extern struct fdc_data fdc_data[NFDC];
240
241 /***********************************************************************\
242 * Per tape drive structure. *
243 \***********************************************************************/
244 static struct ft_data {
245 struct fdc_data *fdc; /* pointer to controller structure */
246 int ftsu; /* this units number on this controller */
247 int type; /* Drive type (Mountain, Colorado) */
248 /* QIC_Geom *ftg; */ /* pointer to Current tape's geometry */
249 int flags;
250 int cmd_wait; /* Command we are awaiting completion of */
251 int sts_wait; /* Tape interrupt status of current request */
252 int io_sts; /* Tape I/O status */
253 int mode;
254 int pcn; /* present cylinder number */
255 int attaching; /* true when ft is attaching */
256 unsigned char *xptr; /* pointer to buffer blk to xfer */
257 int xcnt; /* transfer count */
258 int xblk; /* block number to transfer */
259 int xseg; /* segment being transferred */
260 SegReq *segh; /* Current I/O request */
261 SegReq *segt; /* Tail of queued I/O requests */
262 SegReq *doneh; /* Completed I/O request queue */
263 SegReq *donet; /* Completed I/O request tail */
264 SegReq *segfree; /* Free segments */
265 SegReq *hdr; /* Current tape header */
266 int nsegq; /* Segments on request queue */
267 int ndoneq; /* Segments on completed queue */
268 int nfreelist; /* Segments on free list */
269
270 /* the next 3 should be defines in 'flags' */
271 int active; /* TRUE if transfer is active */
272 int rdonly; /* TRUE if tape is read-only */
273 int newcart; /* TRUE if new cartridge detected */
274 int laststs; /* last reported status code */
275 int lastcfg; /* last reported QIC config */
276 int lasterr; /* last QIC error code */
277 int lastpos; /* last known segment number */
278 int moving; /* TRUE if tape is moving */
279 int rid[7]; /* read_id return values */
280
281 } *ft_data[NFT];
282
283 /***********************************************************************\
284 * Throughout this file the following conventions will be used: *
285 * ft is a pointer to the ft_data struct for the drive in question *
286 * fdc is a pointer to the fdc_data struct for the controller *
287 * ftu is the tape drive unit number *
288 * fdcu is the floppy controller unit number *
289 * ftsu is the tape drive unit number on that controller. (sub-unit) *
290 \***********************************************************************/
291
292
293
294 #define id_physid id_scsiid /* this biotab field doubles as a field */
295 /* for the physical unit number on the controller */
296
297 int ftopen(dev_t, int);
298 int ftclose(dev_t, int);
299 int ftioctl(dev_t, int, caddr_t, int, struct proc *);
300 int ftattach(struct isa_device *, struct isa_device *, int);
301 static timeout_t ft_timeout;
302 static void async_cmd(ftu_t);
303 static void async_req(ftu_t, int);
304 static void async_read(ftu_t, int);
305 static void async_write(ftu_t, int);
306 static void tape_start(ftu_t, int);
307 static void tape_end(ftu_t);
308 static void tape_inactive(ftu_t);
309 static int tape_cmd(ftu_t, int);
310 static int tape_status(ftu_t);
311 static int qic_status(ftu_t, int, int);
312 static int ftreq_rewind(ftu_t);
313 static int ftreq_hwinfo(ftu_t, QIC_HWInfo *);
314
315 /*****************************************************************************/
316
317
318 /*
319 * Allocate a segment I/O buffer from the free list.
320 */
321 static SegReq *
322 segio_alloc(ft_p ft)
323 {
324 SegReq *r;
325
326 /* Grab first item from free list */
327 if ((r = ft->segfree) != NULL) {
328 ft->segfree = ft->segfree->next;
329 ft->nfreelist--;
330 }
331 DPRT(("segio_alloc: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
332 return(r);
333 }
334
335
336 /*
337 * Queue a segment I/O request.
338 */
339 static void
340 segio_queue(ft_p ft, SegReq *sp)
341 {
342 /* Put request on in process queue. */
343 if (ft->segt == NULL)
344 ft->segh = sp;
345 else
346 ft->segt->next = sp;
347 sp->next = NULL;
348 ft->segt = sp;
349 ft->nsegq++;
350 DPRT(("segio_queue: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
351 }
352
353
354 /*
355 * Segment I/O completed, place on correct queue.
356 */
357 static void
358 segio_done(ft_p ft, SegReq *sp)
359 {
360 /* First remove from current I/O queue */
361 ft->segh = sp->next;
362 if (ft->segh == NULL) ft->segt = NULL;
363 ft->nsegq--;
364
365 if (sp->reqtype == FTIO_WRITING) {
366 /* Place on free list */
367 sp->next = ft->segfree;
368 ft->segfree = sp;
369 ft->nfreelist++;
370 wakeup((caddr_t)wc_buff_avail);
371 DPRT(("segio_done: (w) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
372 } else {
373 /* Put on completed I/O queue */
374 if (ft->donet == NULL)
375 ft->doneh = sp;
376 else
377 ft->donet->next = sp;
378 sp->next = NULL;
379 ft->donet = sp;
380 ft->ndoneq++;
381 wakeup((caddr_t)wc_buff_done);
382 DPRT(("segio_done: (r) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
383 }
384 }
385
386
387 /*
388 * Take I/O request from finished queue to free queue.
389 */
390 static void
391 segio_free(ft_p ft, SegReq *sp)
392 {
393 /* First remove from done queue */
394 ft->doneh = sp->next;
395 if (ft->doneh == NULL) ft->donet = NULL;
396 ft->ndoneq--;
397
398 /* Place on free list */
399 sp->next = ft->segfree;
400 ft->segfree = sp;
401 ft->nfreelist++;
402 wakeup((caddr_t)wc_buff_avail);
403 DPRT(("segio_free: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
404 }
405
406 /*
407 * Probe/attach floppy tapes.
408 */
409 int
410 ftattach(isadev, fdup, unithasfd)
411 struct isa_device *isadev, *fdup;
412 int unithasfd;
413 {
414 fdcu_t fdcu = isadev->id_unit; /* fdc active unit */
415 fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */
416 ftu_t ftu = fdup->id_unit;
417 ft_p ft;
418 ftsu_t ftsu = fdup->id_physid;
419 QIC_HWInfo hw;
420 char *manu;
421
422 if (ftu >= NFT) return 0;
423 ft = ft_data[ftu] = malloc(sizeof *ft, M_DEVBUF, M_NOWAIT);
424 bzero(ft, sizeof *ft);
425
426 /* Probe for tape */
427 ft->attaching = 1;
428 ft->type = NO_TYPE;
429 ft->fdc = fdc;
430 ft->ftsu = ftsu;
431
432 /*
433 * FT_NONE - no method, just do it
434 */
435 tape_start(ftu, 0);
436 if (tape_status(ftu) >= 0) {
437 ft->type = FT_NONE;
438 ftreq_hwinfo(ftu, &hw);
439 goto out;
440 }
441
442 /*
443 * FT_COLORADO - colorado style
444 */
445 tape_start(ftu, 0);
446 tape_cmd(ftu, QC_COL_ENABLE1);
447 tape_cmd(ftu, QC_COL_ENABLE2 + ftu);
448 if (tape_status(ftu) >= 0) {
449 ft->type = FT_COLORADO;
450 ftreq_hwinfo(ftu, &hw);
451 tape_cmd(ftu, QC_COL_DISABLE);
452 goto out;
453 }
454
455 /*
456 * FT_MOUNTAIN - mountain style
457 */
458 tape_start(ftu, 0);
459 tape_cmd(ftu, QC_MTN_ENABLE1);
460 tape_cmd(ftu, QC_MTN_ENABLE2);
461 if (tape_status(ftu) >= 0) {
462 ft->type = FT_MOUNTAIN;
463 ftreq_hwinfo(ftu, &hw);
464 tape_cmd(ftu, QC_MTN_DISABLE);
465 goto out;
466 }
467
468 if(isadev->id_flags & FT_PROBE) {
469 /*
470 * Insight probe is dangerous, since it requires the motor being
471 * enabled and therefore risks attached floppy disk drives to jam.
472 * Probe only if explicitly requested by a flag 0x1 from config
473 */
474
475 /*
476 * FT_INSIGHT - insight style
477 *
478 * Since insight requires turning the drive motor on, we will not
479 * perform this probe if a floppy drive was already found with the
480 * the given unit and controller.
481 */
482 if (unithasfd) goto out;
483 tape_start(ftu, 1);
484 if (tape_status(ftu) >= 0) {
485 ft->type = FT_INSIGHT;
486 ftreq_hwinfo(ftu, &hw);
487 goto out;
488 }
489 }
490
491 out:
492 tape_end(ftu);
493 if (ft->type != NO_TYPE) {
494 fdc->flags |= FDC_HASFTAPE;
495 switch(hw.hw_make) {
496 case 0x0000:
497 if (ft->type == FT_COLORADO) {
498 manu = "Colorado";
499 } else if (ft->type == FT_INSIGHT) {
500 manu = "Insight";
501 } else if (ft->type == FT_MOUNTAIN && hw.hw_model == 0x05) {
502 manu = "Archive";
503 } else if (ft->type == FT_MOUNTAIN) {
504 manu = "Mountain";
505 } else {
506 manu = "Unknown";
507 }
508 break;
509 case 0x0001:
510 manu = "Colorado";
511 break;
512 case 0x0005:
513 if (hw.hw_model >= 0x09) {
514 manu = "Conner";
515 } else {
516 manu = "Archive";
517 }
518 break;
519 case 0x0006:
520 manu = "Mountain";
521 break;
522 case 0x0007:
523 manu = "Wangtek";
524 break;
525 case 0x0222:
526 manu = "IOMega";
527 break;
528 default:
529 manu = "Unknown";
530 break;
531 }
532 printf("ft%d: %s tape\n", fdup->id_unit, manu);
533 }
534 ft->attaching = 0;
535 return(ft->type);
536 }
537
538
539 /*
540 * Perform common commands asynchronously.
541 */
542 static void
543 async_cmd(ftu_t ftu) {
544 ft_p ft = ft_data[ftu];
545 fdcu_t fdcu = ft->fdc->fdcu;
546 int cmd, i, st0, st3, pcn;
547 static int bitn, retval, retpos, nbits, newcn;
548 static int wanttrk, wantblk, wantdir;
549 static int curtrk, curblk, curdir, curdiff;
550 static int errcnt = 0;
551
552 restate:
553 #if FTDBGALL
554 DPRT(("async_cmd state: func: %d state: %d\n", async_func, async_state));
555 #endif
556 switch(async_func) {
557 case ACMD_SEEK:
558 /*
559 * Arguments:
560 * 0 - command to perform
561 */
562 switch (async_state) {
563 case 0:
564 cmd = async_arg0;
565 #if FTDBGALL
566 DPRT(("===>async_seek cmd = %d\n", cmd));
567 #endif
568 newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
569 async_state = 1;
570 i = 0;
571 if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1;
572 if (!i && out_fdc(fdcu, ftu) < 0) i = 1;
573 if (!i && out_fdc(fdcu, newcn) < 0) i = 1;
574 if (i) {
575 if (++async_retries >= 10) {
576 DPRT(("ft%d: async_cmd command seek failed!!\n", ftu));
577 goto complete;
578 }
579 DPRT(("ft%d: async_cmd command seek retry...\n",ftu));
580 async_state = 0;
581 goto restate;
582 }
583 break;
584 case 1:
585 out_fdc(fdcu, NE7CMD_SENSEI);
586 st0 = in_fdc(fdcu);
587 pcn = in_fdc(fdcu);
588 if (st0 < 0 || pcn < 0 || newcn != pcn) {
589 if (++async_retries >= 10) {
590 DPRT(("ft%d: async_cmd seek retries exceeded\n",ftu));
591 goto complete;
592 }
593 DPRT(("ft%d: async_cmd command bad st0=$%02x pcn=$%02x\n",
594 ftu, st0, pcn));
595 async_state = 0;
596 timeout(ft_timeout, (caddr_t)ftu, hz/10);
597 break;
598 }
599 if (st0 & 0x20) { /* seek done */
600 ft->pcn = pcn;
601 }
602 #if FTDBGALL
603 else
604 DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n",
605 ftu, st0, pcn));
606 #endif
607 if (async_arg1) goto complete;
608 async_state = 2;
609 timeout(ft_timeout, (caddr_t)ftu, hz/50);
610 break;
611 case 2:
612 goto complete;
613 /* NOTREACHED */
614 }
615 break;
616
617 case ACMD_STATUS:
618 /*
619 * Arguments:
620 * 0 - command to issue report from
621 * 1 - number of bits
622 * modifies: bitn, retval, st3
623 */
624 switch (async_state) {
625 case 0:
626 bitn = 0;
627 retval = 0;
628 cmd = async_arg0;
629 nbits = async_arg1;
630 DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits));
631 CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0);
632 /* NOTREACHED */
633 case 1:
634 out_fdc(fdcu, NE7CMD_SENSED);
635 out_fdc(fdcu, ftu);
636 st3 = in_fdc(fdcu);
637 if (st3 < 0) {
638 DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n",
639 ftu,bitn,retval));
640 async_ret = -1;
641 goto complete;
642 }
643 if ((st3 & 0x10) != 0) retval |= (1 << bitn);
644 bitn++;
645 if (bitn >= (nbits+2)) {
646 if ((retval & 1) && (retval & (1 << (nbits+1)))) {
647 async_ret = (retval & ~(1<<(nbits+1))) >> 1;
648 if (async_arg0 == QC_STATUS && async_arg2 == 0 &&
649 (async_ret & (QS_ERROR|QS_NEWCART))) {
650 async_state = 2;
651 goto restate;
652 }
653 DPRT(("async status got $%04x ($%04x)\n", async_ret,retval));
654 } else {
655 DPRT(("ft%d: async_status failed: retval=$%04x nbits=%d\n",
656 ftu, retval,nbits));
657 async_ret = -2;
658 }
659 goto complete;
660 }
661 CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0);
662 /* NOTREACHED */
663 case 2:
664 if (async_ret & QS_NEWCART) ft->newcart = 1;
665 CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1);
666 case 3:
667 ft->lasterr = async_ret;
668 if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) {
669 DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
670 ftu, ft->lasterr & 0xff, ft->lasterr >> 8));
671 }
672 cmd = async_arg0;
673 nbits = async_arg1;
674 CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1);
675 case 4:
676 goto complete;
677 case 5:
678 CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0);
679 case 6:
680 CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0);
681 case 7:
682 CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0);
683 case 8:
684 cmd = async_arg0;
685 CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
686 }
687 break;
688
689 case ACMD_STATE:
690 /*
691 * Arguments:
692 * 0 - status bits to check
693 */
694 switch(async_state) {
695 case 0:
696 CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0);
697 case 1:
698 if ((async_ret & async_arg0) != 0) goto complete;
699 async_state = 0;
700 if (++async_retries == 360) { /* 90 secs. */
701 DPRT(("ft%d: acmd_state exceeded retry count\n", ftu));
702 goto complete;
703 }
704 timeout(ft_timeout, (caddr_t)ftu, hz/4);
705 break;
706 }
707 break;
708
709 case ACMD_SEEKSTS:
710 /*
711 * Arguments:
712 * 0 - command to perform
713 * 1 - status bits to check
714 * 2 - (optional) seconds to wait until completion
715 */
716 switch(async_state) {
717 case 0:
718 cmd = async_arg0;
719 async_retries = (async_arg2) ? (async_arg2 * 4) : 10;
720 CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
721 case 1:
722 CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0);
723 case 2:
724 if ((async_ret & async_arg1) != 0) goto complete;
725 if (--async_retries == 0) {
726 DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu));
727 goto complete;
728 }
729 async_state = 1;
730 timeout(ft_timeout, (caddr_t)ftu, hz/4);
731 break;
732 }
733 break;
734
735 case ACMD_READID:
736 /*
737 * Arguments: (none)
738 */
739 switch(async_state) {
740 case 0:
741 if (!ft->moving) {
742 CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
743 /* NOTREACHED */
744 }
745 async_state = 1;
746 out_fdc(fdcu, 0x4a); /* READ_ID */
747 out_fdc(fdcu, ftu);
748 break;
749 case 1:
750 for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
751 async_ret = (ft->rid[3]*ftg->g_fdtrk) +
752 (ft->rid[4]*ftg->g_fdside) + ft->rid[5] - 1;
753 DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
754 ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3],
755 ft->rid[4], ft->rid[5], async_ret));
756 if ((ft->rid[0] & 0xc0) != 0 || async_ret < 0) {
757 /*
758 * Method for retry:
759 * errcnt == 1 regular retry
760 * 2 microstep head 1
761 * 3 microstep head 2
762 * 4 microstep head back to 0
763 * 5 fail
764 */
765 if (++errcnt >= 5) {
766 DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu));
767 async_ret = -2;
768 errcnt = 0;
769 goto complete;
770 }
771 if (errcnt == 1) {
772 ft->moving = 0;
773 CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
774 } else {
775 ft->moving = 0;
776 CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0);
777 }
778 DPRT(("readid retry %d...\n", errcnt));
779 async_state = 0;
780 goto restate;
781 }
782 if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) {
783 DPRT(("acmd_readid detected last block on track\n"));
784 retpos = async_ret;
785 CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
786 /* NOTREACHED */
787 }
788 ft->lastpos = async_ret;
789 errcnt = 0;
790 goto complete;
791 /* NOTREACHED */
792 case 2:
793 CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0);
794 case 3:
795 ft->moving = 0;
796 async_ret = retpos+1;
797 goto complete;
798 case 4:
799 CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0);
800 case 5:
801 ft->moving = 1;
802 async_state = 0;
803 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
804 break;
805 }
806 break;
807
808 case ACMD_RUNBLK:
809 /*
810 * Arguments:
811 * 0 - block number I/O will be performed on
812 *
813 * modifies: curpos
814 */
815 switch (async_state) {
816 case 0:
817 wanttrk = async_arg0 / ftg->g_blktrk;
818 wantblk = async_arg0 % ftg->g_blktrk;
819 wantdir = wanttrk & 1;
820 ft->moving = 0;
821 CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
822 case 1:
823 curtrk = wanttrk;
824 curdir = curtrk & 1;
825 DPRT(("Changing to track %d\n", wanttrk));
826 CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
827 case 2:
828 cmd = wanttrk+2;
829 CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0);
830 case 3:
831 CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0);
832 case 4:
833 ft->laststs = async_ret;
834 if (wantblk == 0) {
835 curblk = 0;
836 cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART;
837 CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90);
838 }
839 if (ft->laststs & QS_BOT) {
840 DPRT(("Tape is at BOT\n"));
841 curblk = (wantdir) ? 4800 : 0;
842 async_state = 6;
843 goto restate;
844 }
845 if (ft->laststs & QS_EOT) {
846 DPRT(("Tape is at EOT\n"));
847 curblk = (wantdir) ? 0 : 4800;
848 async_state = 6;
849 goto restate;
850 }
851 CALL_ACMD(5, ACMD_READID, 0, 0, 0);
852 case 5:
853 if (async_ret < 0) {
854 ft->moving = 0;
855 ft->lastpos = -2;
856 if (async_ret == -2) {
857 CALL_ACMD(9, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
858 }
859 CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
860 }
861 curtrk = (async_ret+1) / ftg->g_blktrk;
862 curblk = (async_ret+1) % ftg->g_blktrk;
863 DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n",
864 curtrk, wanttrk, curblk, wantblk));
865 if (curtrk != wanttrk) { /* oops! */
866 DPRT(("oops!! wrong track!\n"));
867 CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
868 }
869 async_state = 6;
870 goto restate;
871 case 6:
872 DPRT(("curtrk = %d nextblk = %d\n", curtrk, curblk));
873 if (curblk == wantblk) {
874 ft->lastpos = curblk - 1;
875 async_ret = ft->lastpos;
876 if (ft->moving) goto complete;
877 CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0);
878 }
879 if (curblk > wantblk) { /* passed it */
880 ft->moving = 0;
881 CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
882 }
883 if ((wantblk - curblk) <= 256) { /* approaching it */
884 CALL_ACMD(5, ACMD_READID, 0, 0, 0);
885 }
886 /* way up ahead */
887 ft->moving = 0;
888 CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
889 break;
890 case 7:
891 ft->moving = 1;
892 CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0);
893 break;
894 case 8:
895 async_state = 9;
896 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
897 break;
898 case 9:
899 goto complete;
900 case 10:
901 curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2;
902 if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1;
903 DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff));
904 CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0);
905 case 11:
906 DPRT(("reverse 1 done\n"));
907 CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
908 case 12:
909 DPRT(("reverse 2 done\n"));
910 CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
911 case 13:
912 CALL_ACMD(5, ACMD_READID, 0, 0, 0);
913 case 14:
914 curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2;
915 if (curdiff < 0) curdiff = 0;
916 DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff));
917 CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0);
918 case 15:
919 DPRT(("forward 1 done\n"));
920 CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
921 case 16:
922 DPRT(("forward 2 done\n"));
923 CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
924 }
925 break;
926 }
927
928 return;
929
930 complete:
931 if (astk_ptr != &astk[0]) {
932 astk_ptr--;
933 async_retries = astk_ptr->over_retries;
934 async_func = astk_ptr->over_func;
935 async_state = astk_ptr->over_state;
936 async_arg0 = astk_ptr->over_arg0;
937 async_arg1 = astk_ptr->over_arg1;
938 async_arg2 = astk_ptr->over_arg2;
939 goto restate;
940 }
941 async_func = ACMD_NONE;
942 async_state = 0;
943 switch (ft->io_sts) {
944 case FTIO_READY:
945 async_req(ftu, 2);
946 break;
947 case FTIO_READING:
948 case FTIO_RDAHEAD:
949 async_read(ftu, 2);
950 break;
951 case FTIO_WRITING:
952 async_write(ftu, 2);
953 break;
954 default:
955 DPRT(("ft%d: bad async_cmd ending I/O state!\n", ftu));
956 break;
957 }
958 }
959
960
961 /*
962 * Entry point for the async request processor.
963 */
964 static void
965 async_req(ftu_t ftu, int from)
966 {
967 ft_p ft = ft_data[ftu];
968 SegReq *sp;
969 static int over_async, lastreq;
970 int cmd;
971
972 if (from == 2) arq_state = over_async;
973
974 restate:
975 switch (arq_state) {
976 case 0: /* Process segment */
977 sp = ft->segh;
978 ft->io_sts = (sp == NULL) ? FTIO_READY : sp->reqtype;
979
980 if (ft->io_sts == FTIO_WRITING)
981 async_write(ftu, from);
982 else
983 async_read(ftu, from);
984 if (ft->io_sts != FTIO_READY) return;
985
986 /* Pull buffer from current I/O queue */
987 if (sp != NULL) {
988 lastreq = sp->reqtype;
989 segio_done(ft, sp);
990
991 /* If I/O cancelled, clear finished queue. */
992 if (sp->reqcan) {
993 while (ft->doneh != NULL)
994 segio_free(ft, ft->doneh);
995 lastreq = FTIO_READY;
996 }
997 } else
998 lastreq = FTIO_READY;
999
1000 /* Detect end of track */
1001 if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) {
1002 ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
1003 }
1004 arq_state = 1;
1005 goto restate;
1006
1007 case 1: /* Next request */
1008 /* If we have another request queued, start it running. */
1009 if (ft->segh != NULL) {
1010 sp = ft->segh;
1011 sp->reqcrc = 0;
1012 arq_state = ard_state = awr_state = 0;
1013 ft->xblk = sp->reqblk;
1014 ft->xseg = sp->reqseg;
1015 ft->xcnt = 0;
1016 ft->xptr = sp->buff;
1017 DPRT(("I/O reqblk = %d\n", ft->xblk));
1018 goto restate;
1019 }
1020
1021 /* If the last request was reading, do read ahead. */
1022 if ((lastreq == FTIO_READING || lastreq == FTIO_RDAHEAD) &&
1023 (sp = segio_alloc(ft)) != NULL) {
1024 sp->reqtype = FTIO_RDAHEAD;
1025 sp->reqblk = ft->xblk;
1026 sp->reqseg = ft->xseg+1;
1027 sp->reqcrc = 0;
1028 sp->reqcan = 0;
1029 segio_queue(ft, sp);
1030 bzero(sp->buff, QCV_SEGSIZE);
1031 arq_state = ard_state = awr_state = 0;
1032 ft->xblk = sp->reqblk;
1033 ft->xseg = sp->reqseg;
1034 ft->xcnt = 0;
1035 ft->xptr = sp->buff;
1036 DPRT(("Processing readahead reqblk = %d\n", ft->xblk));
1037 goto restate;
1038 }
1039
1040 if (ft->moving) {
1041 DPRT(("No more I/O.. Stopping.\n"));
1042 ft->moving = 0;
1043 ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0);
1044 break;
1045 }
1046 arq_state = 7;
1047 goto restate;
1048
1049 case 2: /* End of track */
1050 ft->moving = 0;
1051 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
1052 break;
1053
1054 case 3:
1055 DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk));
1056 ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
1057 break;
1058
1059 case 4:
1060 cmd = (ft->xblk / ftg->g_blktrk) + 2;
1061 if (ft->segh != NULL) {
1062 ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0);
1063 } else {
1064 ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0);
1065 }
1066 break;
1067
1068 case 5:
1069 ft->moving = 1;
1070 ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0);
1071 break;
1072
1073 case 6:
1074 arq_state = 1;
1075 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
1076 break;
1077
1078 case 7:
1079 /* Time to rest. */
1080 ft->active = 0;
1081 ft->lastpos = -2;
1082
1083 /* wakeup those who want an i/o chg */
1084 wakeup((caddr_t)wc_iosts_change);
1085 break;
1086 }
1087 }
1088
1089
1090 /*
1091 * Entry for async read.
1092 */
1093 static void
1094 async_read(ftu_t ftu, int from)
1095 {
1096 ft_p ft = ft_data[ftu];
1097 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1098 int i, rddta[7];
1099 int where;
1100 static int over_async;
1101 static int retries = 0;
1102
1103 if (from == 2) ard_state = over_async;
1104
1105 restate:
1106 #if FTDBGALL
1107 DPRT(("async_read: state: %d from = %d\n", ard_state, from));
1108 #endif
1109 switch (ard_state) {
1110 case 0: /* Start off */
1111 /* If tape is not at desired position, stop and locate */
1112 if (ft->lastpos != (ft->xblk-1)) {
1113 DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
1114 ftu, ft->lastpos, ft->xblk));
1115 ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
1116 }
1117
1118 /* Tape is in position but stopped. */
1119 if (!ft->moving) {
1120 DPRT(("async_read ******STARTING TAPE\n"));
1121 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
1122 }
1123 ard_state = 1;
1124 goto restate;
1125
1126 case 1: /* Start DMA */
1127 /* Tape is now moving and in position-- start DMA now! */
1128 isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2);
1129 out_fdc(fdcu, 0x66); /* read */
1130 out_fdc(fdcu, ftu); /* unit */
1131 out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
1132 out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
1133 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
1134 out_fdc(fdcu, 0x03); /* 1K sectors */
1135 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
1136 out_fdc(fdcu, 0x74); /* gap length */
1137 out_fdc(fdcu, 0xff); /* transfer size */
1138 ard_state = 2;
1139 break;
1140
1141 case 2: /* DMA completed */
1142 /* Transfer complete, get status */
1143 for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
1144 isa_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2);
1145
1146 #if FTDBGALL
1147 /* Compute where the controller thinks we are */
1148 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
1149 + rddta[5]-1;
1150 DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
1151 rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
1152 where, ft->xblk));
1153 #endif
1154
1155 /* Check for errors */
1156 if ((rddta[0] & 0xc0) != 0x00) {
1157 #if !FTDBGALL
1158 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
1159 + rddta[5]-1;
1160 DPRT(("xd: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
1161 rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
1162 where, ft->xblk));
1163 #endif
1164 if ((rddta[1] & 0x04) == 0x04 && retries < 2) {
1165 /* Probably wrong position */
1166 DPRT(("async_read: doing retry %d\n", retries));
1167 ft->lastpos = ft->xblk;
1168 ard_state = 0;
1169 retries++;
1170 goto restate;
1171 } else {
1172 /* CRC/Address-mark/Data-mark, et. al. */
1173 DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk));
1174 ft->segh->reqcrc |= (1 << ft->xcnt);
1175 }
1176 }
1177
1178 /* Otherwise, transfer completed okay. */
1179 retries = 0;
1180 ft->lastpos = ft->xblk;
1181 ft->xblk++;
1182 ft->xcnt++;
1183 ft->xptr += QCV_BLKSIZE;
1184 if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) {
1185 ard_state = 0;
1186 goto restate;
1187 }
1188 DPRT(("Read done.. Cancel = %d\n", ft->segh->reqcan));
1189 ft->io_sts = FTIO_READY;
1190 break;
1191
1192 case 3:
1193 ft->moving = 1;
1194 ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
1195 break;
1196
1197 case 4:
1198 ard_state = 1;
1199 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
1200 break;
1201
1202 default:
1203 DPRT(("ft%d: bad async_read state %d!!\n", ftu, ard_state));
1204 break;
1205 }
1206 }
1207
1208
1209 /*
1210 * Entry for async write. If from is 0, this came from the interrupt
1211 * routine, if it's 1 then it was a timeout, if it's 2, then an
1212 * async_cmd completed.
1213 */
1214 static void
1215 async_write(ftu_t ftu, int from)
1216 {
1217 ft_p ft = ft_data[ftu];
1218 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1219 int i, rddta[7];
1220 int where;
1221 static int over_async;
1222 static int retries = 0;
1223
1224 if (from == 2) awr_state = over_async;
1225
1226 restate:
1227 #if FTDBGALL
1228 DPRT(("async_write: state: %d from = %d\n", awr_state, from));
1229 #endif
1230 switch (awr_state) {
1231 case 0: /* Start off */
1232 /* If tape is not at desired position, stop and locate */
1233 if (ft->lastpos != (ft->xblk-1)) {
1234 DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
1235 ftu, ft->lastpos, ft->xblk));
1236 ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
1237 }
1238
1239 /* Tape is in position but stopped. */
1240 if (!ft->moving) {
1241 DPRT(("async_write ******STARTING TAPE\n"));
1242 ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
1243 }
1244 awr_state = 1;
1245 goto restate;
1246
1247 case 1: /* Start DMA */
1248 /* Tape is now moving and in position-- start DMA now! */
1249 isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
1250 out_fdc(fdcu, 0x45); /* write */
1251 out_fdc(fdcu, ftu); /* unit */
1252 out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cyl */
1253 out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
1254 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
1255 out_fdc(fdcu, 0x03); /* 1K sectors */
1256 out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
1257 out_fdc(fdcu, 0x74); /* gap length */
1258 out_fdc(fdcu, 0xff); /* transfer size */
1259 awr_state = 2;
1260 break;
1261
1262 case 2: /* DMA completed */
1263 /* Transfer complete, get status */
1264 for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
1265 isa_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
1266
1267 #if FTDBGALL
1268 /* Compute where the controller thinks we are */
1269 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
1270 DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
1271 rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
1272 where, ft->xblk));
1273 #endif
1274
1275 /* Check for errors */
1276 if ((rddta[0] & 0xc0) != 0x00) {
1277 #if !FTDBGALL
1278 where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
1279 + rddta[5]-1;
1280 DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
1281 rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
1282 where, ft->xblk));
1283 #endif
1284 if (retries < 3) {
1285 /* Something happened -- try again */
1286 DPRT(("async_write: doing retry %d\n", retries));
1287 ft->lastpos = ft->xblk;
1288 awr_state = 0;
1289 retries++;
1290 goto restate;
1291 } else {
1292 /*
1293 * Retries failed. Note the unrecoverable error.
1294 * Marking the block as bad is useless right now.
1295 */
1296 printf("ft%d: unrecoverable write error on block %d\n",
1297 ftu, ft->xblk);
1298 ft->segh->reqcrc |= (1 << ft->xcnt);
1299 }
1300 }
1301
1302 /* Otherwise, transfer completed okay. */
1303 retries = 0;
1304 ft->lastpos = ft->xblk;
1305 ft->xblk++;
1306 ft->xcnt++;
1307 ft->xptr += QCV_BLKSIZE;
1308 if (ft->xcnt < QCV_BLKSEG) {
1309 awr_state = 0; /* next block */
1310 goto restate;
1311 }
1312 #if FTDBGALL
1313 DPRT(("Write done.\n"));
1314 #endif
1315 ft->io_sts = FTIO_READY;
1316 break;
1317
1318 case 3:
1319 ft->moving = 1;
1320 ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
1321 break;
1322
1323 case 4:
1324 awr_state = 1;
1325 timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
1326 break;
1327
1328 default:
1329 DPRT(("ft%d: bad async_write state %d!!\n", ftu, awr_state));
1330 break;
1331 }
1332 }
1333
1334
1335 /*
1336 * Interrupt handler for active tape. Bounced off of fdintr().
1337 */
1338 int
1339 ftintr(ftu_t ftu)
1340 {
1341 int st0, pcn, i;
1342 ft_p ft = ft_data[ftu];
1343 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1344 int s = splbio();
1345
1346 st0 = 0;
1347 pcn = 0;
1348
1349 /* I/O segment transfer completed */
1350 if (ft->active) {
1351 if (async_func != ACMD_NONE) {
1352 async_cmd(ftu);
1353 splx(s);
1354 return(1);
1355 }
1356 #if FTDBGALL
1357 DPRT(("Got request interrupt\n"));
1358 #endif
1359 async_req(ftu, 0);
1360 splx(s);
1361 return(1);
1362 }
1363
1364 /* Get interrupt status */
1365 if (ft->cmd_wait != FTCMD_READID) {
1366 out_fdc(fdcu, NE7CMD_SENSEI);
1367 st0 = in_fdc(fdcu);
1368 pcn = in_fdc(fdcu);
1369 }
1370
1371 if (ft->cmd_wait == FTCMD_NONE || ft->sts_wait != FTSTS_SNOOZE) {
1372 huh_what:
1373 printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n",
1374 ftu, st0, pcn);
1375 splx(s);
1376 return(1);
1377 }
1378
1379 switch (ft->cmd_wait) {
1380 case FTCMD_RESET:
1381 ft->sts_wait = FTSTS_INTERRUPT;
1382 wakeup((caddr_t)wc_intr_wait);
1383 break;
1384 case FTCMD_RECAL:
1385 case FTCMD_SEEK:
1386 if (st0 & 0x20) { /* seek done */
1387 ft->sts_wait = FTSTS_INTERRUPT;
1388 ft->pcn = pcn;
1389 wakeup((caddr_t)wc_intr_wait);
1390 }
1391 #if FTDBGALL
1392 else
1393 DPRT(("ft%d: seek error st0 = $%02x pcn = %d\n",
1394 ftu, st0, pcn));
1395 #endif
1396 break;
1397 case FTCMD_READID:
1398 for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
1399 ft->sts_wait = FTSTS_INTERRUPT;
1400 wakeup((caddr_t)wc_intr_wait);
1401 break;
1402
1403 default:
1404 goto huh_what;
1405 }
1406
1407 splx(s);
1408 return(1);
1409 }
1410
1411
1412 /*
1413 * Interrupt timeout routine.
1414 */
1415 static void
1416 ft_timeout(void *arg1)
1417 {
1418 int s;
1419 ftu_t ftu = (ftu_t)arg1;
1420 ft_p ft = ft_data[ftu];
1421
1422 s = splbio();
1423 if (ft->active) {
1424 if (async_func != ACMD_NONE) {
1425 async_cmd(ftu);
1426 splx(s);
1427 return;
1428 }
1429 async_req(ftu, 1);
1430 } else {
1431 ft->sts_wait = FTSTS_TIMEOUT;
1432 wakeup((caddr_t)wc_intr_wait);
1433 }
1434 splx(s);
1435 }
1436
1437
1438 /*
1439 * Wait for a particular interrupt to occur. ftintr() will wake us up
1440 * if it sees what we want. Otherwise, time out and return error.
1441 * Should always disable ints before trigger is sent and calling here.
1442 */
1443 static int
1444 ftintr_wait(ftu_t ftu, int cmd, int ticks)
1445 {
1446 int retries, st0, pcn;
1447 ft_p ft = ft_data[ftu];
1448 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1449
1450 ft->cmd_wait = cmd;
1451 ft->sts_wait = FTSTS_SNOOZE;
1452
1453 /* At attach time, we can't rely on having interrupts serviced */
1454 if (ft->attaching) {
1455 switch (cmd) {
1456 case FTCMD_RESET:
1457 DELAY(100);
1458 ft->sts_wait = FTSTS_INTERRUPT;
1459 goto intrdone;
1460 case FTCMD_RECAL:
1461 case FTCMD_SEEK:
1462 for (retries = 0; retries < 10000; retries++) {
1463 DELAY(150);
1464 out_fdc(fdcu, NE7CMD_SENSEI);
1465 st0 = in_fdc(fdcu);
1466 if ((st0 & 0xc0) == 0x80) continue;
1467 pcn = in_fdc(fdcu);
1468 if (st0 & 0x20) {
1469 ft->sts_wait = FTSTS_INTERRUPT;
1470 ft->pcn = pcn;
1471 goto intrdone;
1472 }
1473 }
1474 break;
1475 }
1476 ft->sts_wait = FTSTS_TIMEOUT;
1477 goto intrdone;
1478 }
1479
1480 ftsleep(wc_intr_wait, ticks);
1481
1482 intrdone:
1483 if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */
1484 #if FTDBGALL
1485 if (ft->cmd_wait != FTCMD_RESET)
1486 DPRT(("ft%d: timeout on command %d\n", ftu, ft->cmd_wait));
1487 #endif
1488 ft->cmd_wait = FTCMD_NONE;
1489 ft->sts_wait = FTSTS_NONE;
1490 return(1);
1491 }
1492
1493 /* got interrupt */
1494 if (ft->attaching == 0 && ticks) untimeout(ft_timeout, (caddr_t)ftu);
1495 ft->cmd_wait = FTCMD_NONE;
1496 ft->sts_wait = FTSTS_NONE;
1497 return(0);
1498 }
1499
1500
1501 /*
1502 * Recalibrate tape drive. Parameter totape is true, if we should
1503 * recalibrate to tape drive settings.
1504 */
1505 static int
1506 tape_recal(ftu_t ftu, int totape)
1507 {
1508 int s;
1509 ft_p ft = ft_data[ftu];
1510 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1511
1512 DPRT(("tape_recal start\n"));
1513
1514 #ifdef PC98
1515 outb(0xbe, FDP_FDDEXC | FDP_PORTEXC);
1516 #endif
1517 out_fdc(fdcu, NE7CMD_SPECIFY);
1518 #ifdef PC98
1519 out_fdc(fdcu, (totape) ? 0xEF : 0xCF);
1520 out_fdc(fdcu, 0x02);
1521 #else
1522 out_fdc(fdcu, (totape) ? 0xAD : 0xDF);
1523 out_fdc(fdcu, 0x02);
1524 #endif
1525
1526 s = splbio();
1527 out_fdc(fdcu, NE7CMD_RECAL);
1528 out_fdc(fdcu, ftu);
1529
1530 if (ftintr_wait(ftu, FTCMD_RECAL, hz)) {
1531 splx(s);
1532 DPRT(("ft%d: recalibrate timeout\n", ftu));
1533 return(1);
1534 }
1535 splx(s);
1536
1537 out_fdc(fdcu, NE7CMD_SPECIFY);
1538 #ifdef PC98
1539 out_fdc(fdcu, (totape) ? 0xEF : 0xCF);
1540 out_fdc(fdcu, 0x02);
1541 #else
1542 out_fdc(fdcu, (totape) ? 0xFD : 0xDF);
1543 out_fdc(fdcu, 0x02);
1544 #endif
1545
1546 DPRT(("tape_recal end\n"));
1547 return(0);
1548 }
1549
1550 /*
1551 * Wait for a particular tape status to be met. If all is TRUE, then
1552 * all states must be met, otherwise any state can be met.
1553 */
1554 static int
1555 tape_state(ftu_t ftu, int all, int mask, int seconds)
1556 {
1557 int r, tries, maxtries;
1558
1559 maxtries = (seconds) ? (4 * seconds) : 1;
1560 for (tries = 0; tries < maxtries; tries++) {
1561 r = tape_status(ftu);
1562 if (r >= 0) {
1563 if (all && (r & mask) == mask) return(r);
1564 if ((r & mask) != 0) return(r);
1565 }
1566 if (seconds) ftsleep(wc_long_delay, hz/4);
1567 }
1568 DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n",
1569 ftu, mask, maxtries));
1570 return(-1);
1571 }
1572
1573
1574 /*
1575 * Send a QIC command to tape drive, wait for completion.
1576 */
1577 static int
1578 tape_cmd(ftu_t ftu, int cmd)
1579 {
1580 int newcn;
1581 int retries = 0;
1582 int s;
1583 ft_p ft = ft_data[ftu];
1584 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
1585
1586 DPRT(("===> tape_cmd: %d\n",cmd));
1587 newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
1588
1589 retry:
1590
1591 /* Perform seek */
1592 s = splbio();
1593 out_fdc(fdcu, NE7CMD_SEEK);
1594 out_fdc(fdcu, ftu);
1595 out_fdc(fdcu, newcn);
1596
1597 if (ftintr_wait(ftu, FTCMD_SEEK, hz)) {
1598 DPRT(("ft%d: tape_cmd seek timeout\n", ftu));
1599 redo:
1600 splx(s);
1601 if (++retries < 5) goto retry;
1602 DPRT(("ft%d: tape_cmd seek failed!\n", ftu));
1603 return(1);
1604 }
1605 splx(s);
1606
1607 if (ft->pcn != newcn) {
1608 DPRT(("ft%d: bad seek in tape_cmd; pcn = %d newcn = %d\n",
1609 ftu, ft->pcn, newcn));
1610 goto redo;
1611 }
1612 DELAY(2500);
1613 return(0);
1614 }
1615
1616
1617 /*
1618 * Return status of tape drive
1619 */
1620 static int
1621 tape_status(ftu_t ftu)
1622 {
1623 int r, err, tries;
1624 ft_p ft = ft_data[ftu];
1625 int max = (ft->attaching) ? 2 : 3;
1626
1627 for (r = -1, tries = 0; r < 0 && tries < max; tries++)
1628 r = qic_status(ftu, QC_STATUS, 8);
1629 if (tries == max) return(-1);
1630
1631 recheck:
1632 DPRT(("tape_status got $%04x\n",r));
1633 ft->laststs = r;
1634
1635 if (r & (QS_ERROR|QS_NEWCART)) {
1636 err = qic_status(ftu, QC_ERRCODE, 16);
1637 ft->lasterr = err;
1638 if (r & QS_NEWCART) {
1639 ft->newcart = 1;
1640 /* If tape not referenced, do a seek load point. */
1641 if ((r & QS_FMTOK) == 0 && !ft->attaching) {
1642 tape_cmd(ftu, QC_SEEKLP);
1643 do {
1644 ftsleep(wc_long_delay, hz);
1645 } while ((r = qic_status(ftu, QC_STATUS, 8)) < 0 ||
1646 (r & (QS_READY|QS_CART)) == QS_CART);
1647 goto recheck;
1648 }
1649 } else if (err && !ft->attaching) {
1650 DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
1651 ftu, err & 0xff, err >> 8));
1652 }
1653 r = qic_status(ftu, QC_STATUS, 8);
1654 ft->laststs = r;
1655 DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r));
1656 }
1657
1658 ft->rdonly = (r & QS_RDONLY);
1659 return(r);
1660 }
1661
1662
1663 /*
1664 * Transfer control to tape drive.
1665 */
1666 static void
1667 tape_start(ftu_t ftu, int motor)
1668 {
1669 ft_p ft = ft_data[ftu];
1670 fdc_p fdc = ft->fdc;
1671 int s, mbits;
1672 #ifndef PC98
1673 static int mbmotor[] = { FDO_MOEN0, FDO_MOEN1, FDO_MOEN2, FDO_MOEN3 };
1674 #endif
1675
1676 s = splbio();
1677 DPRT(("tape_start start\n"));
1678
1679 /* reset, dma disable */
1680 #ifdef PC98
1681 outb(fdc->baseport+FDOUT, FDO_RST | FDO_FRY | FDO_AIE | FDO_MTON);
1682 #else
1683 outb(fdc->baseport+FDOUT, 0x00);
1684 #endif
1685 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1686
1687 /* raise reset, enable DMA, motor on if needed */
1688 #ifdef PC98
1689 outb(fdc->baseport+FDOUT, FDO_DMAE | FDO_MTON);
1690 #else
1691 mbits = ftu & 3;
1692 if (motor && ftu < 4)
1693 mbits |= mbmotor[ftu];
1694
1695 outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN | mbits);
1696 #endif
1697 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1698
1699 splx(s);
1700
1701 tape_recal(ftu, 1);
1702
1703 /* set transfer speed */
1704 #ifndef PC98
1705 outb(fdc->baseport+FDCTL, FDC_500KBPS);
1706 DELAY(10);
1707 #endif
1708
1709 DPRT(("tape_start end\n"));
1710 }
1711
1712
1713 /*
1714 * Transfer control back to floppy disks.
1715 */
1716 static void
1717 tape_end(ftu_t ftu)
1718 {
1719 ft_p ft = ft_data[ftu];
1720 fdc_p fdc = ft->fdc;
1721 int s;
1722
1723 DPRT(("tape_end start\n"));
1724 tape_recal(ftu, 0);
1725
1726 s = splbio();
1727
1728 /* reset, dma disable */
1729 #ifdef PC98
1730 outb(fdc->baseport+FDOUT, FDO_RST | FDO_FRY | FDO_AIE | FDO_MTON);
1731 #else
1732 outb(fdc->baseport+FDOUT, 0x00);
1733 #endif
1734 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1735
1736 /* raise reset, enable DMA */
1737 #ifdef PC98
1738 outb(fdc->baseport+FDOUT, FDO_DMAE | FDO_MTON);
1739 #else
1740 outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN);
1741 #endif
1742 (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
1743
1744 splx(s);
1745
1746 /* set transfer speed */
1747 #ifndef PC98
1748 outb(fdc->baseport+FDCTL, FDC_500KBPS);
1749 DELAY(10);
1750 #endif
1751 fdc->flags &= ~FDC_TAPE_BUSY;
1752
1753 DPRT(("tape_end end\n"));
1754 }
1755
1756
1757 /*
1758 * Wait for the driver to go inactive, cancel readahead if necessary.
1759 */
1760 static void
1761 tape_inactive(ftu_t ftu)
1762 {
1763 ft_p ft = ft_data[ftu];
1764 int s = splbio();
1765
1766 if (ft->segh != NULL) {
1767 if (ft->segh->reqtype == FTIO_RDAHEAD) {
1768 /* cancel read-ahead */
1769 ft->segh->reqcan = 1;
1770 } else if (ft->segh->reqtype == FTIO_WRITING && !ft->active) {
1771 /* flush out any remaining writes */
1772 DPRT(("Flushing write I/O chain\n"));
1773 arq_state = ard_state = awr_state = 0;
1774 ft->xblk = ft->segh->reqblk;
1775 ft->xseg = ft->segh->reqseg;
1776 ft->xcnt = 0;
1777 ft->xptr = ft->segh->buff;
1778 ft->active = 1;
1779 timeout(ft_timeout, (caddr_t)ftu, 1);
1780 }
1781 }
1782 while (ft->active) ftsleep(wc_iosts_change, 0);
1783 splx(s);
1784 }
1785
1786
1787 /*
1788 * Get the geometry of the tape currently in the drive.
1789 */
1790 static int
1791 ftgetgeom(ftu_t ftu)
1792 {
1793 int r, i, tries;
1794 int cfg, qic80, ext;
1795 int sts, fmt, len;
1796 ft_p ft = ft_data[ftu];
1797
1798 r = tape_status(ftu);
1799
1800 /* XXX fix me when format mode is finished */
1801 if (r < 0 || (r & QS_CART) == 0 || (r & QS_FMTOK) == 0) {
1802 DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r));
1803 ftg = NULL;
1804 ft->newcart = 1;
1805 return(0);
1806 }
1807
1808 /* Report drive configuration */
1809 for (cfg = -1, tries = 0; cfg < 0 && tries < 3; tries++)
1810 cfg = qic_status(ftu, QC_CONFIG, 8);
1811 if (tries == 3) {
1812 DPRT(("ftgetgeom report config failed\n"));
1813 ftg = NULL;
1814 return(-1);
1815 }
1816 DPRT(("ftgetgeom report config got $%04x\n", cfg));
1817 ft->lastcfg = cfg;
1818
1819 qic80 = cfg & QCF_QIC80;
1820 ext = cfg & QCF_EXTRA;
1821
1822 /*
1823 * XXX - This doesn't seem to work on my Colorado Jumbo 250...
1824 * if it works on your drive, I'd sure like to hear about it.
1825 */
1826 #if 0
1827 /* Report drive status */
1828 for (sts = -1, tries = 0; sts < 0 && tries < 3; tries++)
1829 sts = qic_status(ftu, QC_TSTATUS, 8);
1830 if (tries == 3) {
1831 DPRT(("ftgetgeom report tape status failed\n"));
1832 ftg = NULL;
1833 return(-1);
1834 }
1835 DPRT(("ftgetgeom report tape status got $%04x\n", sts));
1836 #else
1837 /*
1838 * XXX - Forge a fake tape status based upon the returned
1839 * configuration, since the above command or code is broken
1840 * for my drive and probably other older drives.
1841 */
1842 sts = 0;
1843 sts = (qic80) ? QTS_QIC80 : QTS_QIC40;
1844 sts |= (ext) ? QTS_LEN2 : QTS_LEN1;
1845 #endif
1846
1847 fmt = sts & QTS_FMMASK;
1848 len = (sts & QTS_LNMASK) >> 4;
1849
1850 if (fmt > QCV_NFMT) {
1851 ftg = NULL;
1852 printf("ft%d: unsupported tape format\n", ftu);
1853 return(-1);
1854 }
1855 if (len > QCV_NLEN) {
1856 ftg = NULL;
1857 printf("ft%d: unsupported tape length\n", ftu);
1858 return(-1);
1859 }
1860
1861 /* Look up geometry in the table */
1862 for (i = 1; i < NGEOM; i++)
1863 if (ftgtbl[i].g_fmtno == fmt && ftgtbl[i].g_lenno == len) break;
1864 if (i == NGEOM) {
1865 printf("ft%d: unknown tape geometry\n", ftu);
1866 ftg = NULL;
1867 return(-1);
1868 }
1869 ftg = &ftgtbl[i];
1870 if (!ftg->g_trktape) {
1871 printf("ft%d: unsupported format %s w/len %s\n",
1872 ftu, ftg->g_fmtdesc, ftg->g_lendesc);
1873 ftg = NULL;
1874 return(-1);
1875 }
1876 DPRT(("Tape format is %s, length is %s\n", ftg->g_fmtdesc, ftg->g_lendesc));
1877 ft->newcart = 0;
1878 return(0);
1879 }
1880
1881
1882 /*
1883 * Switch between tape/floppy. This will send the tape enable/disable
1884 * codes for this drive's manufacturer.
1885 */
1886 static int
1887 set_fdcmode(dev_t dev, int newmode)
1888 {
1889 ftu_t ftu = FDUNIT(minor(dev));
1890 ft_p ft = ft_data[ftu];
1891 fdc_p fdc = ft->fdc;
1892 static int havebufs = 0;
1893 int i;
1894 SegReq *sp, *rsp;
1895
1896 if (newmode == FDC_TAPE_MODE) {
1897 /* Wake up the tape drive */
1898 switch (ft->type) {
1899 case NO_TYPE:
1900 fdc->flags &= ~FDC_TAPE_BUSY;
1901 return(ENXIO);
1902 case FT_NONE:
1903 tape_start(ftu, 0);
1904 break;
1905 case FT_COLORADO:
1906 tape_start(ftu, 0);
1907 if (tape_cmd(ftu, QC_COL_ENABLE1)) {
1908 tape_end(ftu);
1909 return(EIO);
1910 }
1911 if (tape_cmd(ftu, QC_COL_ENABLE2 + ftu)) {
1912 tape_end(ftu);
1913 return(EIO);
1914 }
1915 break;
1916 case FT_MOUNTAIN:
1917 tape_start(ftu, 0);
1918 if (tape_cmd(ftu, QC_MTN_ENABLE1)) {
1919 tape_end(ftu);
1920 return(EIO);
1921 }
1922 if (tape_cmd(ftu, QC_MTN_ENABLE2)) {
1923 tape_end(ftu);
1924 return(EIO);
1925 }
1926 break;
1927 case FT_INSIGHT:
1928 tape_start(ftu, 1);
1929 break;
1930 default:
1931 DPRT(("ft%d: bad tape type\n", ftu));
1932 return(ENXIO);
1933 }
1934 if (tape_status(ftu) < 0) {
1935 if (ft->type == FT_COLORADO)
1936 tape_cmd(ftu, QC_COL_DISABLE);
1937 else if (ft->type == FT_MOUNTAIN)
1938 tape_cmd(ftu, QC_MTN_DISABLE);
1939 tape_end(ftu);
1940 return(EIO);
1941 }
1942
1943 /* Grab buffers from memory. */
1944 if (!havebufs) {
1945 ft->segh = ft->segt = NULL;
1946 ft->doneh = ft->donet = NULL;
1947 ft->segfree = NULL;
1948 ft->hdr = NULL;
1949 ft->nsegq = ft->ndoneq = ft->nfreelist = 0;
1950 for (i = 0; i < FTNBUFF; i++) {
1951 sp = malloc(sizeof(SegReq), M_DEVBUF, M_WAITOK);
1952 if (sp == NULL) {
1953 printf("ft%d: not enough memory for buffers\n", ftu);
1954 for (sp=ft->segfree; sp != NULL; sp=sp->next)
1955 free(sp, M_DEVBUF);
1956 if (ft->type == FT_COLORADO)
1957 tape_cmd(ftu, QC_COL_DISABLE);
1958 else if (ft->type == FT_MOUNTAIN)
1959 tape_cmd(ftu, QC_MTN_DISABLE);
1960 tape_end(ftu);
1961 return(ENOMEM);
1962 }
1963 sp->reqtype = FTIO_READY;
1964 sp->next = ft->segfree;
1965 ft->segfree = sp;
1966 ft->nfreelist++;
1967 }
1968 /* take one buffer for header */
1969 ft->hdr = ft->segfree;
1970 ft->segfree = ft->segfree->next;
1971 ft->nfreelist--;
1972 havebufs = 1;
1973 }
1974 ft->io_sts = FTIO_READY; /* tape drive is ready */
1975 ft->active = 0; /* interrupt driver not active */
1976 ft->moving = 0; /* tape not moving */
1977 ft->rdonly = 0; /* tape read only */
1978 ft->newcart = 0; /* new cartridge flag */
1979 ft->lastpos = -1; /* tape is rewound */
1980 async_func = ACMD_NONE; /* No async function */
1981 tape_state(ftu, 0, QS_READY, 60);
1982 tape_cmd(ftu, QC_RATE);
1983 tape_cmd(ftu, QCF_RT500+2); /* 500K bps */
1984 tape_state(ftu, 0, QS_READY, 60);
1985 ft->mode = FTM_PRIMARY;
1986 tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */
1987 tape_state(ftu, 0, QS_READY, 60);
1988 ftg = NULL; /* No geometry yet */
1989 ftgetgeom(ftu); /* Get tape geometry */
1990 ftreq_rewind(ftu); /* Make sure tape is rewound */
1991 } else {
1992 if (ft->type == FT_COLORADO)
1993 tape_cmd(ftu, QC_COL_DISABLE);
1994 else if (ft->type == FT_MOUNTAIN)
1995 tape_cmd(ftu, QC_MTN_DISABLE);
1996 tape_end(ftu);
1997 ft->newcart = 0; /* clear new cartridge */
1998 if (ft->hdr != NULL) free(ft->hdr, M_DEVBUF);
1999 if (havebufs) {
2000 for (sp = ft->segfree; sp != NULL;) {
2001 rsp = sp; sp = sp->next;
2002 free(rsp, M_DEVBUF);
2003 }
2004 for (sp = ft->segh; sp != NULL;) {
2005 rsp = sp; sp = sp->next;
2006 free(rsp, M_DEVBUF);
2007 }
2008 for (sp = ft->doneh; sp != NULL;) {
2009 rsp = sp; sp = sp->next;
2010 free(rsp, M_DEVBUF);
2011 }
2012 }
2013 havebufs = 0;
2014 }
2015 return(0);
2016 }
2017
2018
2019 /*
2020 * Perform a QIC status function.
2021 */
2022 static int
2023 qic_status(ftu_t ftu, int cmd, int nbits)
2024 {
2025 int st3, r, i;
2026 ft_p ft = ft_data[ftu];
2027 fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
2028
2029 if (tape_cmd(ftu, cmd)) {
2030 DPRT(("ft%d: QIC status timeout\n", ftu));
2031 return(-1);
2032 }
2033
2034 /* Sense drive status */
2035 out_fdc(fdcu, NE7CMD_SENSED);
2036 out_fdc(fdcu, ftu);
2037 st3 = in_fdc(fdcu);
2038
2039 if ((st3 & 0x10) == 0) { /* track 0 */
2040 DPRT(("qic_status has dead drive... st3 = $%02x\n", st3));
2041 return(-1);
2042 }
2043
2044 for (i = r = 0; i <= nbits; i++) {
2045 if (tape_cmd(ftu, QC_NEXTBIT)) {
2046 DPRT(("ft%d: QIC status bit timed out on %d\n", ftu, i));
2047 return(-1);
2048 }
2049
2050 out_fdc(fdcu, NE7CMD_SENSED);
2051 out_fdc(fdcu, ftu);
2052 st3 = in_fdc(fdcu);
2053 if (st3 < 0) {
2054 DPRT(("ft%d: controller timed out on bit %d r=$%02x\n",
2055 ftu, i, r));
2056 return(-1);
2057 }
2058
2059 r >>= 1;
2060 if (i < nbits)
2061 r |= ((st3 & 0x10) ? 1 : 0) << nbits;
2062 else if ((st3 & 0x10) == 0) {
2063 DPRT(("ft%d: qic status stop bit missing at %d, st3=$%02x r=$%04x\n",
2064 ftu,i,st3,r));
2065 return(-1);
2066 }
2067 }
2068
2069 DPRT(("qic_status returned $%02x\n", r));
2070 return(r);
2071 }
2072
2073
2074 /*
2075 * Open tape drive for use. Bounced off of Fdopen if tape minor is
2076 * detected.
2077 */
2078 int
2079 ftopen(dev_t dev, int arg2) {
2080 ftu_t ftu = FDUNIT(minor(dev));
2081 fdc_p fdc;
2082
2083 /* check bounds */
2084 if (ftu >= NFT)
2085 return(ENXIO);
2086 if (!ft_data[ftu])
2087 return(ENXIO);
2088 fdc = ft_data[ftu]->fdc;
2089 if ((fdc == NULL) || (ft_data[ftu]->type == NO_TYPE))
2090 return(ENXIO);
2091 /* check for controller already busy with tape */
2092 if (fdc->flags & FDC_TAPE_BUSY)
2093 return(EBUSY);
2094 /* make sure we found a tape when probed */
2095 if (!(fdc->flags & FDC_HASFTAPE))
2096 return(ENODEV);
2097 fdc->fdu = ftu;
2098 fdc->flags |= FDC_TAPE_BUSY;
2099 return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */
2100 }
2101
2102
2103 /*
2104 * Close tape and return floppy controller to disk mode.
2105 */
2106 int
2107 ftclose(dev_t dev, int flags)
2108 {
2109 ftu_t ftu = FDUNIT(minor(dev));
2110 ft_p ft = ft_data[ftu];
2111
2112
2113 /* Wait for any remaining I/O activity to complete. */
2114 tape_inactive(ftu);
2115
2116 ft->mode = FTM_PRIMARY;
2117 tape_cmd(ftu, QC_PRIMARY);
2118 tape_state(ftu, 0, QS_READY, 60);
2119 ftreq_rewind(ftu);
2120 return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */
2121 }
2122
2123 /*
2124 * Read or write a segment.
2125 */
2126 static int
2127 ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p)
2128 {
2129 int r, i;
2130 SegReq *sp;
2131 int s;
2132 long blk, bad, seg;
2133 unsigned char *cp, *cp2;
2134 ft_p ft = ft_data[ftu];
2135
2136 if (!ft->active && ft->segh == NULL) {
2137 r = tape_status(ftu);
2138 if ((r & QS_CART) == 0)
2139 return(ENXIO); /* No cartridge */
2140 if ((r & QS_FMTOK) == 0)
2141 return(ENXIO); /* Not formatted */
2142 tape_state(ftu, 0, QS_READY, 90);
2143 }
2144
2145 if (ftg == NULL || ft->newcart) {
2146 tape_inactive(ftu);
2147 tape_state(ftu, 0, QS_READY, 90);
2148 if (ftgetgeom(ftu) < 0)
2149 return(ENXIO);
2150 }
2151
2152 /* Write not allowed on a read-only tape. */
2153 if (cmd == QIOWRITE && ft->rdonly)
2154 return(EROFS);
2155
2156 /* Quick check of request and buffer. */
2157 if (sr == NULL || sr->sg_data == NULL)
2158 return(EINVAL);
2159
2160 /* Make sure requested track and segment is in range. */
2161 if (sr->sg_trk >= ftg->g_trktape || sr->sg_seg >= ftg->g_segtrk)
2162 return(EINVAL);
2163
2164 blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
2165 seg = sr->sg_trk * ftg->g_segtrk + sr->sg_seg;
2166
2167 s = splbio();
2168 if (cmd == QIOREAD) {
2169 /*
2170 * See if the driver is reading ahead.
2171 */
2172 if (ft->doneh != NULL ||
2173 (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD)) {
2174 /*
2175 * Eat the completion queue and see if the request
2176 * is already there.
2177 */
2178 while (ft->doneh != NULL) {
2179 if (blk == ft->doneh->reqblk) {
2180 sp = ft->doneh;
2181 sp->reqtype = FTIO_READING;
2182 sp->reqbad = sr->sg_badmap;
2183 goto rddone;
2184 }
2185 segio_free(ft, ft->doneh);
2186 }
2187
2188 /*
2189 * Not on the completed queue, in progress maybe?
2190 */
2191 if (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD &&
2192 blk == ft->segh->reqblk) {
2193 sp = ft->segh;
2194 sp->reqtype = FTIO_READING;
2195 sp->reqbad = sr->sg_badmap;
2196 goto rdwait;
2197 }
2198 }
2199
2200 /* Wait until we're ready. */
2201 tape_inactive(ftu);
2202
2203 /* Set up a new read request. */
2204 sp = segio_alloc(ft);
2205 sp->reqcrc = 0;
2206 sp->reqbad = sr->sg_badmap;
2207 sp->reqblk = blk;
2208 sp->reqseg = seg;
2209 sp->reqcan = 0;
2210 sp->reqtype = FTIO_READING;
2211 segio_queue(ft, sp);
2212
2213 /* Start the read request off. */
2214 DPRT(("Starting read I/O chain\n"));
2215 arq_state = ard_state = awr_state = 0;
2216 ft->xblk = sp->reqblk;
2217 ft->xseg = sp->reqseg;
2218 ft->xcnt = 0;
2219 ft->xptr = sp->buff;
2220 ft->active = 1;
2221 timeout(ft_timeout, (caddr_t)ftu, 1);
2222
2223 rdwait:
2224 ftsleep(wc_buff_done, 0);
2225
2226 rddone:
2227 bad = sp->reqbad;
2228 sr->sg_crcmap = sp->reqcrc & ~bad;
2229
2230 /* Copy out segment and discard bad mapped blocks. */
2231 cp = sp->buff; cp2 = sr->sg_data;
2232 for (i = 0; i < QCV_BLKSEG; cp += QCV_BLKSIZE, i++) {
2233 if (bad & (1 << i)) continue;
2234 copyout(cp, cp2, QCV_BLKSIZE);
2235 cp2 += QCV_BLKSIZE;
2236 }
2237 segio_free(ft, sp);
2238 } else {
2239 if (ft->segh != NULL && ft->segh->reqtype != FTIO_WRITING)
2240 tape_inactive(ftu);
2241
2242 /* Allocate a buffer and start tape if we're running low. */
2243 sp = segio_alloc(ft);
2244 if (!ft->active && (sp == NULL || ft->nfreelist <= 1)) {
2245 DPRT(("Starting write I/O chain\n"));
2246 arq_state = ard_state = awr_state = 0;
2247 ft->xblk = ft->segh->reqblk;
2248 ft->xseg = ft->segh->reqseg;
2249 ft->xcnt = 0;
2250 ft->xptr = ft->segh->buff;
2251 ft->active = 1;
2252 timeout(ft_timeout, (caddr_t)ftu, 1);
2253 }
2254
2255 /* Sleep until a buffer becomes available. */
2256 while (sp == NULL) {
2257 ftsleep(wc_buff_avail, 0);
2258 sp = segio_alloc(ft);
2259 }
2260
2261 /* Copy in segment and expand bad blocks. */
2262 bad = sr->sg_badmap;
2263 cp = sr->sg_data; cp2 = sp->buff;
2264 for (i = 0; i < QCV_BLKSEG; cp2 += QCV_BLKSIZE, i++) {
2265 if (bad & (1 << i)) continue;
2266 copyin(cp, cp2, QCV_BLKSIZE);
2267 cp += QCV_BLKSIZE;
2268 }
2269 sp->reqblk = blk;
2270 sp->reqseg = seg;
2271 sp->reqcan = 0;
2272 sp->reqtype = FTIO_WRITING;
2273 segio_queue(ft, sp);
2274 }
2275 splx(s);
2276 return(0);
2277 }
2278
2279
2280 /*
2281 * Rewind to beginning of tape
2282 */
2283 static int
2284 ftreq_rewind(ftu_t ftu)
2285 {
2286 ft_p ft = ft_data[ftu];
2287
2288 tape_inactive(ftu);
2289 tape_cmd(ftu, QC_STOP);
2290 tape_state(ftu, 0, QS_READY, 90);
2291 tape_cmd(ftu, QC_SEEKSTART);
2292 tape_state(ftu, 0, QS_READY, 90);
2293 tape_cmd(ftu, QC_SEEKTRACK);
2294 tape_cmd(ftu, 2);
2295 tape_state(ftu, 0, QS_READY, 90);
2296 ft->lastpos = -1;
2297 ft->moving = 0;
2298 return(0);
2299 }
2300
2301
2302 /*
2303 * Move to logical beginning or end of track
2304 */
2305 static int
2306 ftreq_trkpos(ftu_t ftu, int req)
2307 {
2308 int curtrk, r, cmd;
2309 ft_p ft = ft_data[ftu];
2310
2311 tape_inactive(ftu);
2312 tape_cmd(ftu, QC_STOP);
2313 tape_state(ftu, 0, QS_READY, 90);
2314
2315 r = tape_status(ftu);
2316 if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
2317 if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
2318
2319 if (ftg == NULL || ft->newcart) {
2320 if (ftgetgeom(ftu) < 0) return(ENXIO);
2321 }
2322
2323 curtrk = (ft->lastpos < 0) ? 0 : ft->lastpos / ftg->g_blktrk;
2324 if (req == QIOBOT)
2325 cmd = (curtrk & 1) ? QC_SEEKEND : QC_SEEKSTART;
2326 else
2327 cmd = (curtrk & 1) ? QC_SEEKSTART : QC_SEEKEND;
2328 tape_cmd(ftu, cmd);
2329 tape_state(ftu, 0, QS_READY, 90);
2330 return(0);
2331 }
2332
2333
2334 /*
2335 * Seek tape head to a particular track.
2336 */
2337 static int
2338 ftreq_trkset(ftu_t ftu, int *trk)
2339 {
2340 int r;
2341 ft_p ft = ft_data[ftu];
2342
2343 tape_inactive(ftu);
2344 tape_cmd(ftu, QC_STOP);
2345 tape_state(ftu, 0, QS_READY, 90);
2346
2347 r = tape_status(ftu);
2348 if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
2349 if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
2350 if (ftg == NULL || ft->newcart) {
2351 if (ftgetgeom(ftu) < 0) return(ENXIO);
2352 }
2353
2354 tape_cmd(ftu, QC_SEEKTRACK);
2355 tape_cmd(ftu, *trk + 2);
2356 tape_state(ftu, 0, QS_READY, 90);
2357 return(0);
2358 }
2359
2360
2361 /*
2362 * Start tape moving forward.
2363 */
2364 static int
2365 ftreq_lfwd(ftu_t ftu)
2366 {
2367 ft_p ft = ft_data[ftu];
2368
2369 tape_inactive(ftu);
2370 tape_cmd(ftu, QC_STOP);
2371 tape_state(ftu, 0, QS_READY, 90);
2372 tape_cmd(ftu, QC_FORWARD);
2373 ft->moving = 1;
2374 return(0);
2375 }
2376
2377
2378 /*
2379 * Stop the tape
2380 */
2381 static int
2382 ftreq_stop(ftu_t ftu)
2383 {
2384 ft_p ft = ft_data[ftu];
2385
2386 tape_inactive(ftu);
2387 tape_cmd(ftu, QC_STOP);
2388 tape_state(ftu, 0, QS_READY, 90);
2389 ft->moving = 0;
2390 return(0);
2391 }
2392
2393
2394 /*
2395 * Set the particular mode the drive should be in.
2396 */
2397 static int
2398 ftreq_setmode(ftu_t ftu, int cmd)
2399 {
2400 int r;
2401 ft_p ft = ft_data[ftu];
2402
2403 tape_inactive(ftu);
2404 r = tape_status(ftu);
2405
2406 switch(cmd) {
2407 case QIOPRIMARY:
2408 ft->mode = FTM_PRIMARY;
2409 tape_cmd(ftu, QC_PRIMARY);
2410 break;
2411 case QIOFORMAT:
2412 if (r & QS_RDONLY) return(ENXIO);
2413 if ((r & QS_BOT) == 0) return(ENXIO);
2414 tape_cmd(ftu, QC_FORMAT);
2415 break;
2416 case QIOVERIFY:
2417 if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
2418 tape_cmd(ftu, QC_VERIFY);
2419 break;
2420 }
2421 tape_state(ftu, 0, QS_READY, 60);
2422 return(0);
2423 }
2424
2425
2426 /*
2427 * Return drive status bits
2428 */
2429 static int
2430 ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
2431 {
2432 ft_p ft = ft_data[ftu];
2433
2434 if (ft->active)
2435 *sts = ft->laststs & ~QS_READY;
2436 else
2437 *sts = tape_status(ftu);
2438 return(0);
2439 }
2440
2441
2442 /*
2443 * Return drive configuration bits
2444 */
2445 static int
2446 ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p)
2447 {
2448 int r, tries;
2449 ft_p ft = ft_data[ftu];
2450
2451 if (ft->active)
2452 r = ft->lastcfg;
2453 else {
2454 for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
2455 r = qic_status(ftu, QC_CONFIG, 8);
2456 if (tries == 3) return(ENXIO);
2457 }
2458 *cfg = r;
2459 return(0);
2460 }
2461
2462
2463 /*
2464 * Return current tape's geometry.
2465 */
2466 static int
2467 ftreq_geom(ftu_t ftu, QIC_Geom *g)
2468 {
2469 tape_inactive(ftu);
2470 if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO);
2471 bcopy(ftg, g, sizeof(QIC_Geom));
2472 return(0);
2473 }
2474
2475
2476 /*
2477 * Return drive hardware information
2478 */
2479 static int
2480 ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
2481 {
2482 int tries;
2483 int rom, vend;
2484
2485 tape_inactive(ftu);
2486 bzero(hwp, sizeof(QIC_HWInfo));
2487
2488 for (rom = -1, tries = 0; rom < 0 && tries < 3; tries++)
2489 rom = qic_status(ftu, QC_VERSION, 8);
2490 if (rom > 0) {
2491 hwp->hw_rombeta = (rom >> 7) & 0x01;
2492 hwp->hw_romid = rom & 0x7f;
2493 }
2494
2495 for (vend = -1, tries = 0; vend < 0 && tries < 3; tries++)
2496 vend = qic_status(ftu, QC_VENDORID, 16);
2497 if (vend > 0) {
2498 hwp->hw_make = (vend >> 6) & 0x3ff;
2499 hwp->hw_model = vend & 0x3f;
2500 }
2501
2502 return(0);
2503 }
2504
2505
2506 /*
2507 * Receive or Send the in-core header segment.
2508 */
2509 static int
2510 ftreq_hdr(ftu_t ftu, int cmd, QIC_Segment *sp)
2511 {
2512 ft_p ft = ft_data[ftu];
2513 QIC_Header *h = (QIC_Header *)ft->hdr->buff;
2514
2515 if (sp == NULL || sp->sg_data == NULL) return(EINVAL);
2516 if (cmd == QIOSENDHDR) {
2517 copyin(sp->sg_data, ft->hdr->buff, QCV_SEGSIZE);
2518 } else {
2519 if (h->qh_sig != QCV_HDRMAGIC) return(EIO);
2520 copyout(ft->hdr->buff, sp->sg_data, QCV_SEGSIZE);
2521 }
2522 return(0);
2523 }
2524
2525 /*
2526 * I/O functions.
2527 */
2528 int
2529 ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
2530 {
2531 ftu_t ftu = FDUNIT(minor(dev));
2532
2533 switch(cmd) {
2534 case QIOREAD: /* Request reading a segment from tape. */
2535 case QIOWRITE: /* Request writing a segment to tape. */
2536 return(ftreq_rw(ftu, cmd, (QIC_Segment *)data, p));
2537
2538 case QIOREWIND: /* Rewind tape. */
2539 return(ftreq_rewind(ftu));
2540
2541 case QIOBOT: /* Seek to logical beginning of track. */
2542 case QIOEOT: /* Seek to logical end of track. */
2543 return(ftreq_trkpos(ftu, cmd));
2544
2545 case QIOTRACK: /* Seek tape head to specified track. */
2546 return(ftreq_trkset(ftu, (int *)data));
2547
2548 case QIOSEEKLP: /* Seek load point. */
2549 goto badreq;
2550
2551 case QIOFORWARD: /* Move tape in logical forward direction. */
2552 return(ftreq_lfwd(ftu));
2553
2554 case QIOSTOP: /* Causes tape to stop. */
2555 return(ftreq_stop(ftu));
2556
2557 case QIOPRIMARY: /* Enter primary mode. */
2558 case QIOFORMAT: /* Enter format mode. */
2559 case QIOVERIFY: /* Enter verify mode. */
2560 return(ftreq_setmode(ftu, cmd));
2561
2562 case QIOWRREF: /* Write reference burst. */
2563 goto badreq;
2564
2565 case QIOSTATUS: /* Get drive status. */
2566 return(ftreq_status(ftu, cmd, (int *)data, p));
2567
2568 case QIOCONFIG: /* Get tape configuration. */
2569 return(ftreq_config(ftu, cmd, (int *)data, p));
2570
2571 case QIOGEOM:
2572 return(ftreq_geom(ftu, (QIC_Geom *)data));
2573
2574 case QIOHWINFO:
2575 return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data));
2576
2577 case QIOSENDHDR:
2578 case QIORECVHDR:
2579 return(ftreq_hdr(ftu, cmd, (QIC_Segment *)data));
2580 }
2581 badreq:
2582 DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd));
2583 return(ENXIO);
2584 }
2585
2586 #endif
Cache object: 47a151ad70c2ac81d74f81ce62ea35a9
|