Bump up the SYMTAB_SPACE for LINT. It was already too small again.
[unix-history] / sys / i386 / isa / wt.c
CommitLineData
df9b2252
RG
1/*
2 * Streamer tape driver for 386bsd and FreeBSD.
7a8b8432 3 * Supports Archive and Wangtek compatible QIC-02/QIC-36 boards.
df9b2252
RG
4 *
5 * Copyright (C) 1993 by:
6 * Sergey Ryzhkov <sir@kiae.su>
7 * Serge Vakulenko <vak@zebub.msk.su>
8 *
7a8b8432 9 * This software is distributed with NO WARRANTIES, not even the implied
df9b2252 10 * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15637ed4 11 *
df9b2252
RG
12 * Authors grant any other persons or organisations permission to use
13 * or modify this software as long as this message is kept with the software,
14 * all derivative works or modified versions.
15637ed4 15 *
df9b2252
RG
16 * This driver is derived from the old 386bsd Wangtek streamer tape driver,
17 * made by Robert Baron at CMU, based on Intel sources.
18 * Authors thank Robert Baron, CMU and Intel and retain here
19 * the original CMU copyright notice.
d7136515 20 *
7a8b8432 21 * Version 1.3, Thu Nov 11 12:09:13 MSK 1993
fde1aeb2 22 * $Id: wt.c,v 1.4 1993/12/13 18:38:43 alm Exp $
7a8b8432 23 *
15637ed4
RG
24 */
25
26/*
15637ed4
RG
27 * Copyright (c) 1989 Carnegie-Mellon University.
28 * All rights reserved.
29 *
30 * Authors: Robert Baron
31 *
32 * Permission to use, copy, modify and distribute this software and
33 * its documentation is hereby granted, provided that both the copyright
34 * notice and this permission notice appear in all copies of the
35 * software, derivative works or modified versions, and any portions
36 * thereof, and that both notices appear in supporting documentation.
37 *
38 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
39 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
40 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
41 *
42 * Carnegie Mellon requests users of this software to return to
43 *
44 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
45 * School of Computer Science
46 * Carnegie Mellon University
47 * Pittsburgh PA 15213-3890
48 *
49 * any improvements or extensions that they make and grant Carnegie the
50 * rights to redistribute these changes.
51 */
52
53#include "wt.h"
54#if NWT > 0
15637ed4 55
15637ed4 56#include "sys/param.h"
fde1aeb2
GW
57#include "systm.h"
58#include "kernel.h"
15637ed4 59#include "sys/buf.h"
df9b2252
RG
60#include "sys/fcntl.h"
61#include "sys/malloc.h"
62#include "sys/ioctl.h"
63#include "sys/mtio.h"
64#include "vm/vm_param.h"
65#include "i386/include/pio.h"
66#include "i386/isa/isa_device.h"
15637ed4
RG
67#include "i386/isa/wtreg.h"
68
7a8b8432
AM
69/*
70 * Uncomment this to enable internal device tracing.
71 */
72#define DEBUG(s) /* printf s */
73
df9b2252 74#define WTPRI (PZERO+10) /* sleep priority */
15637ed4
RG
75
76/*
df9b2252 77 * Wangtek controller ports
15637ed4 78 */
df9b2252
RG
79#define WT_CTLPORT(base) ((base)+0) /* control, write only */
80#define WT_STATPORT(base) ((base)+0) /* status, read only */
81#define WT_CMDPORT(base) ((base)+1) /* command, write only */
82#define WT_DATAPORT(base) ((base)+1) /* data, read only */
83#define WT_NPORT 2 /* 2 i/o ports */
84
85/* status port bits */
86#define WT_BUSY 0x01 /* not ready bit define */
87#define WT_NOEXCEP 0x02 /* no exception bit define */
88#define WT_RESETMASK 0x07 /* to check after reset */
89#define WT_RESETVAL 0x05 /* state after reset */
90
91/* control port bits */
92#define WT_ONLINE 0x01 /* device selected */
93#define WT_RESET 0x02 /* reset command */
94#define WT_REQUEST 0x04 /* request command */
7a8b8432 95#define WT_IEN 0x08 /* enable dma */
15637ed4 96
df9b2252
RG
97/*
98 * Archive controller ports
99 */
100#define AV_DATAPORT(base) ((base)+0) /* data, read only */
101#define AV_CMDPORT(base) ((base)+0) /* command, write only */
102#define AV_STATPORT(base) ((base)+1) /* status, read only */
103#define AV_CTLPORT(base) ((base)+1) /* control, write only */
104#define AV_SDMAPORT(base) ((base)+2) /* start dma */
105#define AV_RDMAPORT(base) ((base)+3) /* reset dma */
106#define AV_NPORT 4 /* 4 i/o ports */
107
108/* status port bits */
109#define AV_BUSY 0x40 /* not ready bit define */
110#define AV_NOEXCEP 0x20 /* no exception bit define */
111#define AV_RESETMASK 0xf8 /* to check after reset */
112#define AV_RESETVAL 0x50 /* state after reset */
113
114/* control port bits */
115#define AV_RESET 0x80 /* reset command */
116#define AV_REQUEST 0x40 /* request command */
117#define AV_IEN 0x20 /* enable interrupts */
118
7a8b8432
AM
119enum wttype {
120 UNKNOWN = 0, /* unknown type, driver disabled */
121 ARCHIVE, /* Archive Viper SC499, SC402 etc */
122 WANGTEK, /* Wangtek */
123};
df9b2252
RG
124
125typedef struct {
126 unsigned short err; /* code for error encountered */
127 unsigned short ercnt; /* number of error blocks */
128 unsigned short urcnt; /* number of underruns */
129} wtstatus_t;
130
131typedef struct {
7a8b8432 132 enum wttype type; /* type of controller */
df9b2252
RG
133 unsigned unit; /* unit number */
134 unsigned port; /* base i/o port */
135 unsigned chan; /* dma channel number, 1..3 */
136 unsigned flags; /* state of tape drive */
137 unsigned dens; /* tape density */
7a8b8432 138 int bsize; /* tape block size */
df9b2252
RG
139 void *buf; /* internal i/o buffer */
140
141 void *dmavaddr; /* virtual address of dma i/o buffer */
142 unsigned dmatotal; /* size of i/o buffer */
143 unsigned dmaflags; /* i/o direction, B_READ or B_WRITE */
144 unsigned dmacount; /* resulting length of dma i/o */
145
146 wtstatus_t error; /* status of controller */
147
148 unsigned short DATAPORT, CMDPORT, STATPORT, CTLPORT, SDMAPORT, RDMAPORT;
149 unsigned char BUSY, NOEXCEP, RESETMASK, RESETVAL;
150 unsigned char ONLINE, RESET, REQUEST, IEN;
151} wtinfo_t;
152
153wtinfo_t wttab[NWT]; /* tape info by unit number */
154
df9b2252
RG
155static int wtwait (wtinfo_t *t, int catch, char *msg);
156static int wtcmd (wtinfo_t *t, int cmd);
157static int wtstart (wtinfo_t *t, unsigned mode, void *vaddr, unsigned len);
158static void wtdma (wtinfo_t *t);
fde1aeb2 159static void wtimer (caddr_t, int);
df9b2252
RG
160static void wtclock (wtinfo_t *t);
161static int wtreset (wtinfo_t *t);
7a8b8432 162static int wtsense (wtinfo_t *t, int verb, int ignor);
df9b2252
RG
163static int wtstatus (wtinfo_t *t);
164static void wtrewind (wtinfo_t *t);
165static int wtreadfm (wtinfo_t *t);
166static int wtwritefm (wtinfo_t *t);
7a8b8432 167static int wtpoll (wtinfo_t *t, int mask, int bits);
df9b2252 168
fde1aeb2 169/* XXX */
df9b2252 170extern void DELAY (int usec);
15637ed4 171
df9b2252
RG
172/*
173 * Probe for the presence of the device.
174 */
175int wtprobe (struct isa_device *id)
176{
177 wtinfo_t *t = wttab + id->id_unit;
178
179 t->unit = id->id_unit;
180 t->chan = id->id_drq;
7a8b8432 181 t->port = id->id_iobase;
df9b2252
RG
182 if (t->chan<1 || t->chan>3) {
183 printf ("wt%d: Bad drq=%d, should be 1..3\n", t->unit, t->chan);
184 return (0);
185 }
df9b2252
RG
186
187 /* Try Wangtek. */
7a8b8432 188 t->type = WANGTEK;
df9b2252
RG
189 t->CTLPORT = WT_CTLPORT (t->port); t->STATPORT = WT_STATPORT (t->port);
190 t->CMDPORT = WT_CMDPORT (t->port); t->DATAPORT = WT_DATAPORT (t->port);
191 t->SDMAPORT = 0; t->RDMAPORT = 0;
192 t->BUSY = WT_BUSY; t->NOEXCEP = WT_NOEXCEP;
193 t->RESETMASK = WT_RESETMASK; t->RESETVAL = WT_RESETVAL;
194 t->ONLINE = WT_ONLINE; t->RESET = WT_RESET;
7a8b8432 195 t->REQUEST = WT_REQUEST; t->IEN = WT_IEN;
df9b2252
RG
196 if (wtreset (t))
197 return (WT_NPORT);
198
199 /* Try Archive. */
7a8b8432 200 t->type = ARCHIVE;
df9b2252
RG
201 t->CTLPORT = AV_CTLPORT (t->port); t->STATPORT = AV_STATPORT (t->port);
202 t->CMDPORT = AV_CMDPORT (t->port); t->DATAPORT = AV_DATAPORT (t->port);
203 t->SDMAPORT = AV_SDMAPORT (t->port); t->RDMAPORT = AV_RDMAPORT (t->port);
204 t->BUSY = AV_BUSY; t->NOEXCEP = AV_NOEXCEP;
205 t->RESETMASK = AV_RESETMASK; t->RESETVAL = AV_RESETVAL;
206 t->ONLINE = 0; t->RESET = AV_RESET;
207 t->REQUEST = AV_REQUEST; t->IEN = AV_IEN;
208 if (wtreset (t))
209 return (AV_NPORT);
210
211 /* Tape controller not found. */
7a8b8432 212 t->type = UNKNOWN;
df9b2252 213 return (0);
15637ed4
RG
214}
215
216/*
df9b2252 217 * Device is found, configure it.
15637ed4 218 */
df9b2252 219int wtattach (struct isa_device *id)
15637ed4 220{
df9b2252 221 wtinfo_t *t = wttab + id->id_unit;
15637ed4 222
7a8b8432 223 if (t->type == ARCHIVE) {
df9b2252
RG
224 printf ("wt%d: type <Archive>\n", t->unit);
225 outb (t->RDMAPORT, 0); /* reset dma */
226 } else
227 printf ("wt%d: type <Wangtek>\n", t->unit);
228 t->flags = TPSTART; /* tape is rewound */
229 t->dens = -1; /* unknown density */
df9b2252 230 return (1);
15637ed4
RG
231}
232
df9b2252 233struct isa_driver wtdriver = { wtprobe, wtattach, "wt", };
15637ed4 234
df9b2252 235int wtdump (int dev)
15637ed4 236{
df9b2252
RG
237 /* Not implemented */
238 return (EINVAL);
15637ed4
RG
239}
240
df9b2252 241int wtsize (int dev)
15637ed4 242{
df9b2252
RG
243 /* Not implemented */
244 return (-1);
15637ed4
RG
245}
246
247/*
df9b2252 248 * Open routine, called on every device open.
15637ed4 249 */
df9b2252
RG
250int wtopen (int dev, int flag)
251{
252 int u = minor (dev) & T_UNIT;
253 wtinfo_t *t = wttab + u;
254 int error;
255
7a8b8432 256 if (u >= NWT || t->type == UNKNOWN)
df9b2252
RG
257 return (ENXIO);
258
259 /* Check that device is not in use */
260 if (t->flags & TPINUSE)
261 return (EBUSY);
262
263 /* If the tape is in rewound state, check the status and set density. */
264 if (t->flags & TPSTART) {
265 /* If rewind is going on, wait */
7a8b8432 266 if (error = wtwait (t, PCATCH, "wtrew"))
df9b2252
RG
267 return (error);
268
7a8b8432
AM
269 /* Check the controller status */
270 if (! wtsense (t, 0, (flag & FWRITE) ? 0 : TP_WRP)) {
271 /* Bad status, reset the controller */
df9b2252 272 if (! wtreset (t))
7a8b8432
AM
273 return (EIO);
274 if (! wtsense (t, 1, (flag & FWRITE) ? 0 : TP_WRP))
275 return (EIO);
15637ed4 276 }
df9b2252
RG
277
278 /* Set up tape density. */
7a8b8432
AM
279 if (t->dens != (minor (dev) & WT_DENSEL)) {
280 int d = 0;
281
282 switch (minor (dev) & WT_DENSEL) {
283 case WT_DENSDFLT: default: break; /* default density */
284 case WT_QIC11: d = QIC_FMT11; break; /* minor 010 */
285 case WT_QIC24: d = QIC_FMT24; break; /* minor 020 */
286 case WT_QIC120: d = QIC_FMT120; break; /* minor 030 */
287 case WT_QIC150: d = QIC_FMT150; break; /* minor 040 */
288 case WT_QIC300: d = QIC_FMT300; break; /* minor 050 */
289 case WT_QIC600: d = QIC_FMT600; break; /* minor 060 */
df9b2252 290 }
7a8b8432
AM
291 if (d) {
292 /* Change tape density. */
293 if (! wtcmd (t, d))
294 return (EIO);
295 if (! wtsense (t, 1, TP_WRP | TP_ILL))
296 return (EIO);
297
298 /* Check the status of the controller. */
299 if (t->error.err & TP_ILL) {
300 printf ("wt%d: invalid tape density\n", t->unit);
301 return (ENODEV);
302 }
303 }
304 t->dens = minor (dev) & WT_DENSEL;
15637ed4 305 }
df9b2252 306 t->flags &= ~TPSTART;
7a8b8432 307 } else if (t->dens != (minor (dev) & WT_DENSEL))
df9b2252 308 return (ENXIO);
15637ed4 309
7a8b8432
AM
310 t->bsize = (minor (dev) & WT_BSIZE) ? 1024 : 512;
311 t->buf = malloc (t->bsize, M_TEMP, M_WAITOK);
312 if (! t->buf)
313 return (EAGAIN);
314
df9b2252 315 t->flags = TPINUSE;
15637ed4 316 if (flag & FREAD)
df9b2252 317 t->flags |= TPREAD;
15637ed4 318 if (flag & FWRITE)
df9b2252
RG
319 t->flags |= TPWRITE;
320 return (0);
15637ed4
RG
321}
322
323/*
df9b2252 324 * Close routine, called on last device close.
15637ed4 325 */
df9b2252 326int wtclose (int dev)
15637ed4 327{
df9b2252
RG
328 int u = minor (dev) & T_UNIT;
329 wtinfo_t *t = wttab + u;
15637ed4 330
7a8b8432 331 if (u >= NWT || t->type == UNKNOWN)
df9b2252 332 return (ENXIO);
15637ed4 333
df9b2252
RG
334 /* If rewind is pending, do nothing */
335 if (t->flags & TPREW)
336 goto done;
15637ed4 337
df9b2252 338 /* If seek forward is pending and no rewind on close, do nothing */
7a8b8432
AM
339 if (t->flags & TPRMARK) {
340 if (minor (dev) & T_NOREWIND)
341 goto done;
15637ed4 342
7a8b8432
AM
343 /* If read file mark is going on, wait */
344 wtwait (t, 0, "wtrfm");
345 }
15637ed4 346
df9b2252
RG
347 if (t->flags & TPWANY)
348 /* Tape was written. Write file mark. */
349 wtwritefm (t);
15637ed4 350
df9b2252
RG
351 if (! (minor (dev) & T_NOREWIND)) {
352 /* Rewind tape to beginning of tape. */
353 /* Don't wait until rewind, though. */
354 wtrewind (t);
355 goto done;
15637ed4 356 }
df9b2252
RG
357 if ((t->flags & TPRANY) && ! (t->flags & (TPVOL | TPWANY)))
358 /* Space forward to after next file mark if no writing done. */
359 /* Don't wait for completion. */
360 wtreadfm (t);
361done:
362 t->flags &= TPREW | TPRMARK | TPSTART | TPTIMER;
7a8b8432 363 free (t->buf, M_TEMP);
df9b2252 364 return (0);
15637ed4
RG
365}
366
df9b2252
RG
367/*
368 * Ioctl routine. Compatible with BSD ioctls.
369 * Direct QIC-02 commands ERASE and RETENSION added.
370 * There are three possible ioctls:
371 * ioctl (int fd, MTIOCGET, struct mtget *buf) -- get status
372 * ioctl (int fd, MTIOCTOP, struct mtop *buf) -- do BSD-like op
373 * ioctl (int fd, WTQICMD, int qicop) -- do QIC op
374 */
375int wtioctl (int dev, int cmd, void *arg, int mode)
376{
377 int u = minor (dev) & T_UNIT;
378 wtinfo_t *t = wttab + u;
379 int error, count, op;
380
7a8b8432 381 if (u >= NWT || t->type == UNKNOWN)
df9b2252
RG
382 return (ENXIO);
383
384 switch (cmd) {
385 default:
386 return (EINVAL);
387 case WTQICMD: /* direct QIC command */
388 op = (int) *(void**)arg;
389 switch (op) {
390 default:
391 return (EINVAL);
392 case QIC_ERASE: /* erase the whole tape */
393 if (! (t->flags & TPWRITE) || (t->flags & TPWP))
394 return (EACCES);
395 if (error = wtwait (t, PCATCH, "wterase"))
396 return (error);
15637ed4 397 break;
df9b2252
RG
398 case QIC_RETENS: /* retension the tape */
399 if (error = wtwait (t, PCATCH, "wtretens"))
400 return (error);
15637ed4 401 break;
df9b2252
RG
402 }
403 /* Both ERASE and RETENS operations work like REWIND. */
404 /* Simulate the rewind operation here. */
405 t->flags &= ~(TPRO | TPWO | TPVOL);
406 if (! wtcmd (t, op))
407 return (EIO);
408 t->flags |= TPSTART | TPREW;
409 if (op == QIC_ERASE)
410 t->flags |= TPWANY;
411 wtclock (t);
412 return (0);
413 case MTIOCIEOT: /* ignore EOT errors */
414 case MTIOCEEOT: /* enable EOT errors */
415 return (0);
416 case MTIOCGET:
7a8b8432
AM
417 ((struct mtget*)arg)->mt_type =
418 t->type == ARCHIVE ? MT_ISVIPER1 : 0x11;
df9b2252
RG
419 ((struct mtget*)arg)->mt_dsreg = t->flags; /* status */
420 ((struct mtget*)arg)->mt_erreg = t->error.err; /* errors */
421 ((struct mtget*)arg)->mt_resid = 0;
422 ((struct mtget*)arg)->mt_fileno = 0; /* file */
423 ((struct mtget*)arg)->mt_blkno = 0; /* block */
424 return (0);
425 case MTIOCTOP:
426 break;
15637ed4 427 }
df9b2252
RG
428 switch ((short) ((struct mtop*)arg)->mt_op) {
429 default:
430 case MTFSR: /* forward space record */
431 case MTBSR: /* backward space record */
432 case MTBSF: /* backward space file */
433 break;
434 case MTNOP: /* no operation, sets status only */
435 case MTCACHE: /* enable controller cache */
436 case MTNOCACHE: /* disable controller cache */
437 return (0);
438 case MTREW: /* rewind */
439 case MTOFFL: /* rewind and put the drive offline */
440 if (t->flags & TPREW) /* rewind is running */
441 return (0);
442 if (error = wtwait (t, PCATCH, "wtorew"))
443 return (error);
444 wtrewind (t);
445 return (0);
446 case MTFSF: /* forward space file */
447 for (count=((struct mtop*)arg)->mt_count; count>0; --count) {
448 if (error = wtwait (t, PCATCH, "wtorfm"))
449 return (error);
450 if (error = wtreadfm (t))
451 return (error);
15637ed4 452 }
df9b2252
RG
453 return (0);
454 case MTWEOF: /* write an end-of-file record */
455 if (! (t->flags & TPWRITE) || (t->flags & TPWP))
456 return (EACCES);
457 if (error = wtwait (t, PCATCH, "wtowfm"))
458 return (error);
459 if (error = wtwritefm (t))
460 return (error);
461 return (0);
15637ed4 462 }
df9b2252 463 return (EINVAL);
15637ed4
RG
464}
465
df9b2252
RG
466/*
467 * Strategy routine.
468 */
469void wtstrategy (struct buf *bp)
15637ed4 470{
df9b2252
RG
471 int u = minor (bp->b_dev) & T_UNIT;
472 wtinfo_t *t = wttab + u;
473 int s;
15637ed4 474
df9b2252 475 bp->b_resid = bp->b_bcount;
7a8b8432 476 if (u >= NWT || t->type == UNKNOWN)
df9b2252 477 goto errxit;
15637ed4 478
df9b2252
RG
479 /* at file marks and end of tape, we just return '0 bytes available' */
480 if (t->flags & TPVOL)
481 goto xit;
15637ed4 482
df9b2252
RG
483 if (bp->b_flags & B_READ) {
484 /* Check read access and no previous write to this tape. */
485 if (! (t->flags & TPREAD) || (t->flags & TPWANY))
486 goto errxit;
15637ed4 487
df9b2252
RG
488 /* For now, we assume that all data will be copied out */
489 /* If read command outstanding, just skip down */
490 if (! (t->flags & TPRO)) {
7a8b8432 491 if (! wtsense (t, 1, TP_WRP)) /* clear status */
df9b2252
RG
492 goto errxit;
493 if (! wtcmd (t, QIC_RDDATA)) { /* sed read mode */
7a8b8432 494 wtsense (t, 1, TP_WRP);
df9b2252
RG
495 goto errxit;
496 }
497 t->flags |= TPRO | TPRANY;
498 }
499 } else {
500 /* Check write access and write protection. */
501 /* No previous read from this tape allowed. */
502 if (! (t->flags & TPWRITE) || (t->flags & (TPWP | TPRANY)))
503 goto errxit;
15637ed4 504
df9b2252
RG
505 /* If write command outstanding, just skip down */
506 if (! (t->flags & TPWO)) {
7a8b8432 507 if (! wtsense (t, 1, 0)) /* clear status */
df9b2252
RG
508 goto errxit;
509 if (! wtcmd (t, QIC_WRTDATA)) { /* set write mode */
7a8b8432 510 wtsense (t, 1, 0);
df9b2252
RG
511 goto errxit;
512 }
513 t->flags |= TPWO | TPWANY;
514 }
515 }
15637ed4 516
df9b2252
RG
517 if (! bp->b_bcount)
518 goto xit;
15637ed4 519
df9b2252
RG
520 t->flags &= ~TPEXCEP;
521 s = splbio ();
522 if (wtstart (t, bp->b_flags, bp->b_un.b_addr, bp->b_bcount)) {
523 wtwait (t, 0, (bp->b_flags & B_READ) ? "wtread" : "wtwrite");
524 bp->b_resid -= t->dmacount;
525 }
526 splx (s);
15637ed4 527
df9b2252
RG
528 if (t->flags & TPEXCEP) {
529errxit: bp->b_flags |= B_ERROR;
530 bp->b_error = EIO;
531 }
532xit: biodone (bp);
533 return;
15637ed4
RG
534}
535
df9b2252
RG
536/*
537 * Interrupt routine.
538 */
539void wtintr (int u)
15637ed4 540{
df9b2252
RG
541 wtinfo_t *t = wttab + u;
542 unsigned char s;
15637ed4 543
7a8b8432
AM
544 if (u >= NWT || t->type == UNKNOWN) {
545 DEBUG (("wtintr() -- device not configured\n"));
df9b2252 546 return;
7a8b8432 547 }
15637ed4 548
df9b2252 549 s = inb (t->STATPORT); /* get status */
7a8b8432
AM
550 DEBUG (("wtintr() status=0x%x -- ", s));
551 if ((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP)) {
552 DEBUG (("busy\n"));
df9b2252 553 return; /* device is busy */
7a8b8432 554 }
15637ed4 555
df9b2252
RG
556 /*
557 * Check if rewind finished.
558 */
559 if (t->flags & TPREW) {
7a8b8432
AM
560 DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ?
561 "rewind busy?\n" : "rewind finished\n"));
df9b2252 562 t->flags &= ~TPREW; /* Rewind finished. */
7a8b8432 563 wtsense (t, 1, TP_WRP);
fde1aeb2 564 wakeup ((caddr_t)t);
df9b2252
RG
565 return;
566 }
15637ed4 567
df9b2252
RG
568 /*
569 * Check if writing/reading of file mark finished.
570 */
571 if (t->flags & (TPRMARK | TPWMARK)) {
7a8b8432
AM
572 DEBUG (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ?
573 "marker r/w busy?\n" : "marker r/w finished\n"));
574 if (! (s & t->NOEXCEP)) /* operation failed */
575 wtsense (t, 1, (t->flags & TPRMARK) ? TP_WRP : 0);
576 t->flags &= ~(TPRMARK | TPWMARK); /* operation finished */
fde1aeb2 577 wakeup ((caddr_t)t);
df9b2252
RG
578 return;
579 }
15637ed4 580
df9b2252
RG
581 /*
582 * Do we started any i/o? If no, just return.
583 */
7a8b8432
AM
584 if (! (t->flags & TPACTIVE)) {
585 DEBUG (("unexpected interrupt\n"));
df9b2252 586 return;
7a8b8432 587 }
df9b2252 588 t->flags &= ~TPACTIVE;
7a8b8432 589 t->dmacount += t->bsize; /* increment counter */
15637ed4 590
df9b2252
RG
591 /*
592 * Clean up dma.
593 */
7a8b8432
AM
594 if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize) {
595 /* If reading short block, copy the internal buffer
596 * to the user memory. */
597 isa_dmadone (t->dmaflags, t->buf, t->bsize, t->chan);
df9b2252
RG
598 bcopy (t->buf, t->dmavaddr, t->dmatotal - t->dmacount);
599 } else
7a8b8432 600 isa_dmadone (t->dmaflags, t->dmavaddr, t->bsize, t->chan);
df9b2252
RG
601
602 /*
603 * On exception, check for end of file and end of volume.
604 */
605 if (! (s & t->NOEXCEP)) {
7a8b8432
AM
606 DEBUG (("i/o exception\n"));
607 wtsense (t, 1, (t->dmaflags & B_READ) ? TP_WRP : 0);
df9b2252
RG
608 if (t->error.err & (TP_EOM | TP_FIL))
609 t->flags |= TPVOL; /* end of file */
610 else
611 t->flags |= TPEXCEP; /* i/o error */
fde1aeb2 612 wakeup ((caddr_t)t);
df9b2252
RG
613 return;
614 }
15637ed4 615
df9b2252 616 if (t->dmacount < t->dmatotal) { /* continue i/o */
7a8b8432 617 t->dmavaddr += t->bsize;
df9b2252 618 wtdma (t);
7a8b8432 619 DEBUG (("continue i/o, %d\n", t->dmacount));
df9b2252
RG
620 return;
621 }
622 if (t->dmacount > t->dmatotal) /* short last block */
623 t->dmacount = t->dmatotal;
fde1aeb2 624 wakeup ((caddr_t)t); /* wake up user level */
7a8b8432 625 DEBUG (("i/o finished, %d\n", t->dmacount));
15637ed4
RG
626}
627
df9b2252
RG
628/* start the rewind operation */
629static void wtrewind (wtinfo_t *t)
15637ed4 630{
7a8b8432
AM
631 int rwmode = (t->flags & (TPRO | TPWO));
632
df9b2252 633 t->flags &= ~(TPRO | TPWO | TPVOL);
7a8b8432
AM
634 /*
635 * Wangtek strictly follows QIC-02 standard:
636 * clearing ONLINE in read/write modes causes rewind.
637 * REWIND command is not allowed in read/write mode
638 * and gives `illegal command' error.
639 */
640 if (t->type==WANGTEK && rwmode) {
641 outb (t->CTLPORT, 0);
642 } else if (! wtcmd (t, QIC_REWIND))
df9b2252
RG
643 return;
644 t->flags |= TPSTART | TPREW;
645 wtclock (t);
15637ed4
RG
646}
647
df9b2252
RG
648/* start the `read marker' operation */
649static int wtreadfm (wtinfo_t *t)
15637ed4 650{
df9b2252
RG
651 t->flags &= ~(TPRO | TPWO | TPVOL);
652 if (! wtcmd (t, QIC_READFM)) {
7a8b8432 653 wtsense (t, 1, TP_WRP);
df9b2252
RG
654 return (EIO);
655 }
656 t->flags |= TPRMARK | TPRANY;
657 wtclock (t);
658 /* Don't wait for completion here. */
659 return (0);
15637ed4
RG
660}
661
df9b2252
RG
662/* write marker to the tape */
663static int wtwritefm (wtinfo_t *t)
15637ed4 664{
fde1aeb2 665 tsleep ((caddr_t)wtwritefm, WTPRI, "wtwfm", hz); /* timeout: 1 second */
df9b2252
RG
666 t->flags &= ~(TPRO | TPWO);
667 if (! wtcmd (t, QIC_WRITEFM)) {
7a8b8432 668 wtsense (t, 1, 0);
df9b2252 669 return (EIO);
15637ed4 670 }
df9b2252
RG
671 t->flags |= TPWMARK | TPWANY;
672 wtclock (t);
673 return (wtwait (t, 0, "wtwfm"));
15637ed4
RG
674}
675
7a8b8432
AM
676/* while controller status & mask == bits continue waiting */
677static int wtpoll (wtinfo_t *t, int mask, int bits)
15637ed4 678{
7a8b8432
AM
679 int s, i;
680
681 /* Poll status port, waiting for specified bits. */
682 for (i=0; i<1000; ++i) { /* up to 1 msec */
683 s = inb (t->STATPORT);
684 if ((s & mask) != bits)
685 return (s);
686 DELAY (1);
687 }
688 for (i=0; i<100; ++i) { /* up to 10 msec */
689 s = inb (t->STATPORT);
690 if ((s & mask) != bits)
691 return (s);
692 DELAY (100);
693 }
694 for (;;) { /* forever */
695 s = inb (t->STATPORT);
696 if ((s & mask) != bits)
697 return (s);
fde1aeb2 698 tsleep ((caddr_t)wtpoll, WTPRI, "wtpoll", 1); /* timeout: 1 tick */
7a8b8432 699 }
15637ed4
RG
700}
701
df9b2252
RG
702/* execute QIC command */
703static int wtcmd (wtinfo_t *t, int cmd)
15637ed4 704{
7a8b8432
AM
705 int s;
706
707 DEBUG (("wtcmd() cmd=0x%x\n", cmd));
708 s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
709 if (! (s & t->NOEXCEP)) /* error */
710 return (0);
df9b2252
RG
711
712 outb (t->CMDPORT, cmd); /* output the command */
15637ed4 713
df9b2252 714 outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
7a8b8432 715 wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */
df9b2252 716 outb (t->CTLPORT, t->IEN | t->ONLINE); /* reset request */
7a8b8432 717 wtpoll (t, t->BUSY, 0); /* wait for not ready */
df9b2252 718 return (1);
15637ed4
RG
719}
720
df9b2252
RG
721/* wait for the end of i/o, seeking marker or rewind operation */
722static int wtwait (wtinfo_t *t, int catch, char *msg)
15637ed4 723{
df9b2252 724 int error;
15637ed4 725
7a8b8432 726 DEBUG (("wtwait() `%s'\n", msg));
df9b2252 727 while (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))
fde1aeb2 728 if (error = tsleep ((caddr_t)t, WTPRI | catch, msg, 0))
df9b2252
RG
729 return (error);
730 return (0);
15637ed4
RG
731}
732
df9b2252
RG
733/* initialize dma for the i/o operation */
734static void wtdma (wtinfo_t *t)
15637ed4 735{
df9b2252
RG
736 t->flags |= TPACTIVE;
737 wtclock (t);
738
7a8b8432 739 if (t->type == ARCHIVE)
df9b2252
RG
740 outb (t->SDMAPORT, 0); /* set dma */
741
7a8b8432 742 if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize)
df9b2252 743 /* Reading short block. Do it through the internal buffer. */
7a8b8432 744 isa_dmastart (t->dmaflags, t->buf, t->bsize, t->chan);
15637ed4 745 else
7a8b8432 746 isa_dmastart (t->dmaflags, t->dmavaddr, t->bsize, t->chan);
15637ed4
RG
747}
748
df9b2252
RG
749/* start i/o operation */
750static int wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len)
15637ed4 751{
7a8b8432
AM
752 int s;
753
754 DEBUG (("wtstart()\n"));
755 s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
756 if (! (s & t->NOEXCEP)) {
df9b2252
RG
757 t->flags |= TPEXCEP; /* error */
758 return (0);
759 }
760 t->flags &= ~TPEXCEP; /* clear exception flag */
761 t->dmavaddr = vaddr;
762 t->dmatotal = len;
763 t->dmacount = 0;
764 t->dmaflags = flags;
765 wtdma (t);
766 return (1);
15637ed4
RG
767}
768
df9b2252
RG
769/* start timer */
770static void wtclock (wtinfo_t *t)
15637ed4 771{
df9b2252
RG
772 if (! (t->flags & TPTIMER)) {
773 t->flags |= TPTIMER;
7a8b8432
AM
774 /* Some controllers seem to lose dma interrupts too often.
775 * To make the tape stream we need 1 tick timeout. */
fde1aeb2 776 timeout (wtimer, (caddr_t)t, (t->flags & TPACTIVE) ? 1 : hz);
df9b2252 777 }
15637ed4
RG
778}
779
df9b2252
RG
780/*
781 * Simulate an interrupt periodically while i/o is going.
782 * This is necessary in case interrupts get eaten due to
783 * multiple devices on a single IRQ line.
784 */
fde1aeb2 785static void wtimer (caddr_t xt, int dummy)
15637ed4 786{
fde1aeb2 787 wtinfo_t *t = (wtinfo_t *)xt;
df9b2252 788 int s;
15637ed4 789
df9b2252
RG
790 t->flags &= ~TPTIMER;
791 if (! (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)))
792 return;
15637ed4 793
df9b2252
RG
794 /* If i/o going, simulate interrupt. */
795 s = splbio ();
7a8b8432
AM
796 if ((inb (t->STATPORT) & (t->BUSY | t->NOEXCEP)) != (t->BUSY | t->NOEXCEP)) {
797 DEBUG (("wtimer() -- "));
798 wtintr (t->unit);
799 }
df9b2252
RG
800 splx (s);
801
802 /* Restart timer if i/o pending. */
803 if (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))
804 wtclock (t);
805}
806
807/* reset the controller */
808static int wtreset (wtinfo_t *t)
809{
7a8b8432
AM
810 /* Perform QIC-02 and QIC-36 compatible reset sequence. */
811 /* Thanks to Mikael Hybsch <micke@dynas.se>. */
812 int s, i;
813
814 outb (t->CTLPORT, t->RESET | t->ONLINE); /* send reset */
815 DELAY (30);
816 outb (t->CTLPORT, t->ONLINE); /* turn off reset */
817 DELAY (30);
818
819 /* Read the controller status. */
820 s = inb (t->STATPORT);
821 if (s == 0xff) /* no port at this address? */
df9b2252 822 return (0);
7a8b8432
AM
823
824 /* Wait 3 sec for reset to complete. Needed for QIC-36 boards? */
825 for (i=0; i<3000; ++i) {
826 if (! (s & t->BUSY) || ! (s & t->NOEXCEP))
827 break;
828 DELAY (1000);
829 s = inb (t->STATPORT);
830 }
831 return ((s & t->RESETMASK) == t->RESETVAL);
df9b2252
RG
832}
833
834/* get controller status information */
835/* return 0 if user i/o request should receive an i/o error code */
7a8b8432 836static int wtsense (wtinfo_t *t, int verb, int ignor)
df9b2252
RG
837{
838 char *msg = 0;
839 int err;
840
7a8b8432 841 DEBUG (("wtsense() ignor=0x%x\n", ignor));
df9b2252
RG
842 t->flags &= ~(TPRO | TPWO);
843 if (! wtstatus (t))
844 return (0);
845 if (! (t->error.err & TP_ST0))
846 t->error.err &= ~TP_ST0MASK;
847 if (! (t->error.err & TP_ST1))
848 t->error.err &= ~TP_ST1MASK;
849 t->error.err &= ~ignor; /* ignore certain errors */
850 err = t->error.err & (TP_FIL | TP_BNL | TP_UDA | TP_EOM | TP_WRP |
851 TP_USL | TP_CNI | TP_MBD | TP_NDT | TP_ILL);
852 if (! err)
853 return (1);
7a8b8432
AM
854 if (! verb)
855 return (0);
df9b2252
RG
856
857 /* lifted from tdriver.c from Wangtek */
858 if (err & TP_USL) msg = "Drive not online";
859 else if (err & TP_CNI) msg = "No cartridge";
860 else if ((err & TP_WRP) && !(t->flags & TPWP)) {
861 msg = "Tape is write protected";
862 t->flags |= TPWP;
863 }
864 else if (err & TP_FIL) msg = 0 /*"Filemark detected"*/;
865 else if (err & TP_EOM) msg = 0 /*"End of tape"*/;
866 else if (err & TP_BNL) msg = "Block not located";
867 else if (err & TP_UDA) msg = "Unrecoverable data error";
868 else if (err & TP_NDT) msg = "No data detected";
869 else if (err & TP_ILL) msg = "Illegal command";
870 if (msg)
871 printf ("wt%d: %s\n", t->unit, msg);
872 return (0);
873}
874
875/* get controller status information */
876static int wtstatus (wtinfo_t *t)
877{
878 char *p;
879
7a8b8432 880 wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */
df9b2252
RG
881 outb (t->CMDPORT, QIC_RDSTAT); /* send `read status' command */
882
883 outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
7a8b8432 884 wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */
df9b2252 885 outb (t->CTLPORT, t->ONLINE); /* reset request */
7a8b8432 886 wtpoll (t, t->BUSY, 0); /* wait for not ready */
df9b2252
RG
887
888 p = (char*) &t->error;
889 while (p < (char*)&t->error + 6) {
7a8b8432
AM
890 int s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP);
891 if (! (s & t->NOEXCEP)) /* error */
892 return (0);
df9b2252
RG
893
894 *p++ = inb (t->DATAPORT); /* read status byte */
895
7a8b8432
AM
896 outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */
897 wtpoll (t, t->BUSY, 0); /* wait for not ready */
898 outb (t->CTLPORT, t->ONLINE); /* unset request */
df9b2252
RG
899 }
900 return (1);
901}
902#endif /* NWT */