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