Commit | Line | Data |
---|---|---|
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 |
119 | enum wttype { |
120 | UNKNOWN = 0, /* unknown type, driver disabled */ | |
121 | ARCHIVE, /* Archive Viper SC499, SC402 etc */ | |
122 | WANGTEK, /* Wangtek */ | |
123 | }; | |
df9b2252 RG |
124 | |
125 | typedef 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 | ||
131 | typedef 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 | ||
153 | wtinfo_t wttab[NWT]; /* tape info by unit number */ | |
154 | ||
df9b2252 RG |
155 | static int wtwait (wtinfo_t *t, int catch, char *msg); |
156 | static int wtcmd (wtinfo_t *t, int cmd); | |
157 | static int wtstart (wtinfo_t *t, unsigned mode, void *vaddr, unsigned len); | |
158 | static void wtdma (wtinfo_t *t); | |
fde1aeb2 | 159 | static void wtimer (caddr_t, int); |
df9b2252 RG |
160 | static void wtclock (wtinfo_t *t); |
161 | static int wtreset (wtinfo_t *t); | |
7a8b8432 | 162 | static int wtsense (wtinfo_t *t, int verb, int ignor); |
df9b2252 RG |
163 | static int wtstatus (wtinfo_t *t); |
164 | static void wtrewind (wtinfo_t *t); | |
165 | static int wtreadfm (wtinfo_t *t); | |
166 | static int wtwritefm (wtinfo_t *t); | |
7a8b8432 | 167 | static int wtpoll (wtinfo_t *t, int mask, int bits); |
df9b2252 | 168 | |
fde1aeb2 | 169 | /* XXX */ |
df9b2252 | 170 | extern void DELAY (int usec); |
15637ed4 | 171 | |
df9b2252 RG |
172 | /* |
173 | * Probe for the presence of the device. | |
174 | */ | |
175 | int 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 | 219 | int 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 | 233 | struct isa_driver wtdriver = { wtprobe, wtattach, "wt", }; |
15637ed4 | 234 | |
df9b2252 | 235 | int wtdump (int dev) |
15637ed4 | 236 | { |
df9b2252 RG |
237 | /* Not implemented */ |
238 | return (EINVAL); | |
15637ed4 RG |
239 | } |
240 | ||
df9b2252 | 241 | int 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 |
250 | int 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 | 326 | int 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); | |
361 | done: | |
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 | */ | |
375 | int 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 | */ | |
469 | void 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) { |
529 | errxit: bp->b_flags |= B_ERROR; | |
530 | bp->b_error = EIO; | |
531 | } | |
532 | xit: biodone (bp); | |
533 | return; | |
15637ed4 RG |
534 | } |
535 | ||
df9b2252 RG |
536 | /* |
537 | * Interrupt routine. | |
538 | */ | |
539 | void 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 */ |
629 | static 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 */ |
649 | static 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 */ |
663 | static 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 */ |
677 | static 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 */ |
703 | static 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 */ |
722 | static 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 */ |
734 | static 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 */ |
750 | static 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 */ |
770 | static 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 | 785 | static 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 */ | |
808 | static 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 | 836 | static 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 */ | |
876 | static 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 */ |