Commit | Line | Data |
---|---|---|
b28deaf8 | 1 | /* ts.c 4.5 %G% */ |
9bad5ea5 | 2 | |
66b4fb09 | 3 | #include "ts.h" |
9bad5ea5 BJ |
4 | #if NTS > 0 |
5 | /* | |
6 | * TS11 tape driver | |
7 | */ | |
8 | ||
9 | #include "../h/param.h" | |
10 | #include "../h/systm.h" | |
11 | #include "../h/buf.h" | |
12 | #include "../h/conf.h" | |
13 | #include "../h/dir.h" | |
14 | #include "../h/file.h" | |
15 | #include "../h/user.h" | |
16 | #include "../h/pte.h" | |
17 | #include "../h/map.h" | |
18 | #include "../h/uba.h" | |
e1b765e1 | 19 | #include "../h/vm.h" |
9bad5ea5 | 20 | |
9bad5ea5 | 21 | struct device { |
f0a3ddbd BJ |
22 | u_short tsdb; |
23 | u_short tssr; | |
9bad5ea5 BJ |
24 | }; |
25 | ||
26 | struct buf tstab; | |
27 | struct buf rtsbuf; | |
28 | struct buf ctsbuf; | |
29 | ||
30 | #define INF 1000000000 | |
31 | ||
f0a3ddbd | 32 | u_short ts_uba; |
b28deaf8 | 33 | int ts_ubinfo; |
9bad5ea5 BJ |
34 | char ts_flags; |
35 | char ts_openf; | |
36 | daddr_t ts_blkno; | |
37 | daddr_t ts_nxrec; | |
38 | ||
39 | /* status message */ | |
40 | struct sts { | |
f0a3ddbd | 41 | u_short s_sts; |
e1b765e1 BJ |
42 | u_short len; |
43 | u_short rbpcr; | |
f0a3ddbd BJ |
44 | u_short xs0; |
45 | u_short xs1; | |
46 | u_short xs2; | |
47 | u_short xs3; | |
9bad5ea5 BJ |
48 | }; |
49 | ||
50 | /* Error codes in stat 0 */ | |
51 | #define TMK 0100000 | |
52 | #define RLS 040000 | |
53 | #define ONL 0100 | |
54 | #define WLE 04000 | |
55 | ||
56 | /* command message */ | |
57 | struct cmd { | |
f0a3ddbd BJ |
58 | u_short c_cmd; |
59 | u_short c_loba; | |
60 | u_short c_hiba; | |
61 | u_short c_size; | |
9bad5ea5 BJ |
62 | }; |
63 | ||
64 | #define ACK 0100000 | |
65 | #define CVC 040000 | |
66 | #define IE 0200 | |
67 | #define READ 01 | |
68 | #define REREAD 01001 | |
69 | ||
70 | #define SETCHR 04 | |
71 | ||
72 | #define WRITE 05 | |
73 | #define REWRITE 01005 | |
74 | ||
75 | #define SFORW 010 | |
76 | #define SREV 0410 | |
77 | #define REW 02010 | |
78 | ||
79 | #define WTM 011 | |
80 | ||
81 | #define GSTAT 017 | |
82 | ||
83 | /* characteristics data */ | |
84 | struct charac { | |
f0a3ddbd BJ |
85 | u_short char_loba; |
86 | u_short char_hiba; | |
87 | u_short char_size; | |
88 | u_short char_mode; | |
9bad5ea5 BJ |
89 | }; |
90 | ||
91 | /* All the packets, collected */ | |
92 | struct tsmesg { | |
93 | struct cmd ts_cmd; | |
94 | struct sts ts_sts; | |
95 | struct charac ts_char; | |
96 | int align; /* Should force alignment */ | |
97 | } ts; | |
98 | ||
99 | /* Bits in (unibus) status register */ | |
100 | #define SC 0100000 | |
101 | #define SSR 0200 | |
102 | #define OFL 0100 | |
103 | #define NBA 02000 | |
104 | ||
105 | /* states */ | |
106 | #define SIO 1 | |
107 | #define SSFOR 2 | |
108 | #define SSREV 3 | |
109 | #define SRETRY 4 | |
110 | #define SCOM 5 | |
111 | #define SOK 6 | |
112 | ||
113 | #define H_WRITTEN 1 | |
114 | ||
115 | tsopen(dev, flag) | |
116 | { | |
117 | register struct device *tsaddr = TSADDR; | |
118 | static struct tsmesg *ubaddr; | |
119 | ||
120 | tstab.b_flags |= B_TAPE; | |
121 | if (ts_openf) { | |
122 | u.u_error = ENXIO; | |
123 | return; | |
124 | } | |
125 | if (ubaddr==0 || tsaddr->tssr&(OFL|NBA) || (tsaddr->tssr&SSR)==0) { | |
126 | long i = 0; | |
127 | tsaddr->tssr = 0; | |
128 | while ((tsaddr->tssr & SSR)==0) { | |
129 | if (++i > 1000000) { | |
130 | printf("Tape unready\n"); | |
131 | u.u_error = ENXIO; | |
132 | return; | |
133 | } | |
134 | } | |
135 | } | |
136 | if (tsaddr->tssr&OFL) { | |
137 | printf("Tape offline\n"); | |
138 | u.u_error = ENXIO; | |
139 | return; | |
140 | } | |
141 | if (tsaddr->tssr&NBA) { | |
142 | ctsbuf.b_un.b_addr = (caddr_t) &ts; | |
143 | ctsbuf.b_bcount = sizeof(ts); | |
144 | if (ubaddr == 0) | |
145 | ubaddr = (struct tsmesg *)ubasetup(&ctsbuf, 0); | |
f0a3ddbd | 146 | ts_uba = (u_short)((long)ubaddr + (((long)ubaddr >> 16) & 03)); |
9bad5ea5 | 147 | ts.ts_char.char_loba = (int)&ubaddr->ts_sts; |
f0a3ddbd | 148 | ts.ts_char.char_hiba = (u_short)((long)&ubaddr->ts_sts >> 16) & 03; |
9bad5ea5 BJ |
149 | ts.ts_char.char_size = sizeof(ts.ts_sts); |
150 | ts.ts_char.char_mode = 0400; /* Stop on 2 tape marks */ | |
151 | ts.ts_cmd.c_cmd = ACK + 04; /* write characteristics */ | |
152 | ts.ts_cmd.c_loba = (int)&ubaddr->ts_char; | |
f0a3ddbd | 153 | ts.ts_cmd.c_hiba = (u_short)((long)&ubaddr->ts_sts >> 16) & 03; |
9bad5ea5 BJ |
154 | ts.ts_cmd.c_size = sizeof(ts.ts_sts); |
155 | tsaddr->tsdb = ts_uba; | |
156 | } | |
157 | ts_blkno = 0; | |
158 | ts_nxrec = INF; | |
159 | ts_flags = 0; | |
160 | if (u.u_error==0) | |
161 | ts_openf++; | |
162 | } | |
163 | ||
164 | tsclose(dev, flag) | |
165 | { | |
166 | ||
167 | if (flag == FWRITE || ((flag&FWRITE) && (ts_flags&H_WRITTEN))) { | |
168 | tscommand(WTM); | |
169 | tscommand(WTM); | |
170 | tscommand(SREV); | |
171 | } | |
172 | if ((minor(dev)&4) == 0) | |
173 | tscommand(REW); | |
174 | ts_openf = 0; | |
175 | } | |
176 | ||
177 | tscommand(com) | |
178 | { | |
179 | register struct buf *bp; | |
180 | ||
181 | bp = &ctsbuf; | |
182 | spl5(); | |
183 | while(bp->b_flags&B_BUSY) { | |
184 | bp->b_flags |= B_WANTED; | |
185 | sleep((caddr_t)bp, PRIBIO); | |
186 | } | |
187 | spl0(); | |
188 | bp->b_resid = com; | |
189 | bp->b_blkno = 0; | |
190 | bp->b_flags = B_BUSY|B_READ; | |
191 | tsstrategy(bp); | |
192 | iowait(bp); | |
193 | if(bp->b_flags&B_WANTED) | |
194 | wakeup((caddr_t)bp); | |
195 | bp->b_flags = 0; | |
196 | return(bp->b_resid); | |
197 | } | |
198 | ||
199 | tsstrategy(bp) | |
200 | register struct buf *bp; | |
201 | { | |
202 | register daddr_t *p; | |
203 | ||
204 | if(bp != &ctsbuf) { | |
205 | p = &ts_nxrec; | |
206 | if(dbtofsb(bp->b_blkno) > *p) { | |
207 | bp->b_flags |= B_ERROR; | |
208 | bp->b_error = ENXIO; | |
209 | iodone(bp); | |
210 | return; | |
211 | } | |
212 | if(dbtofsb(bp->b_blkno) == *p && bp->b_flags&B_READ) { | |
213 | bp->b_resid = bp->b_bcount; | |
214 | iodone(bp); | |
215 | return; | |
216 | } | |
217 | if ((bp->b_flags&B_READ)==0) { | |
218 | *p = dbtofsb(bp->b_blkno) + 1; | |
219 | ts_flags |= H_WRITTEN; | |
220 | } | |
221 | } | |
222 | bp->av_forw = NULL; | |
223 | spl5(); | |
224 | if (tstab.b_actf == NULL) | |
225 | tstab.b_actf = bp; | |
226 | else | |
227 | tstab.b_actl->av_forw = bp; | |
228 | tstab.b_actl = bp; | |
229 | if (tstab.b_active==0) | |
230 | tsstart(); | |
231 | spl0(); | |
232 | } | |
233 | ||
234 | tsstart() | |
235 | { | |
236 | register struct buf *bp; | |
237 | register struct device *tsaddr = TSADDR; | |
238 | daddr_t blkno; | |
239 | ||
240 | loop: | |
241 | if ((bp = tstab.b_actf) == NULL) | |
242 | return; | |
243 | blkno = ts_blkno; | |
244 | if (ts_openf < 0 || dbtofsb(bp->b_blkno) > ts_nxrec) | |
245 | goto abort; | |
246 | if (bp == &ctsbuf) { | |
247 | tstab.b_active = SCOM; | |
248 | ts.ts_cmd.c_cmd = ACK+CVC+IE+bp->b_resid; | |
249 | ts.ts_cmd.c_loba = 1; /* count always 1 */ | |
250 | } else if (blkno == dbtofsb(bp->b_blkno)) { | |
251 | tstab.b_active = SIO; | |
b28deaf8 BJ |
252 | ts_ubinfo = ubasetup(bp, 1); |
253 | ts.ts_cmd.c_loba = (u_short)ts_ubinfo; | |
254 | ts.ts_cmd.c_hiba = (u_short)(ts_ubinfo >> 16) & 03; | |
9bad5ea5 BJ |
255 | ts.ts_cmd.c_size = bp->b_bcount; |
256 | if(bp->b_flags & B_READ) | |
257 | ts.ts_cmd.c_cmd = ACK+CVC+IE+READ; | |
258 | else | |
259 | ts.ts_cmd.c_cmd = ACK+CVC+IE+WRITE; | |
260 | } else { | |
261 | if (blkno < dbtofsb(bp->b_blkno)) { | |
262 | tstab.b_active = SSFOR; | |
263 | ts.ts_cmd.c_cmd = ACK+CVC+IE+SFORW; | |
264 | ts.ts_cmd.c_loba = dbtofsb(bp->b_blkno) - blkno; | |
265 | } else { | |
266 | tstab.b_active = SSREV; | |
267 | ts.ts_cmd.c_cmd = ACK+CVC+IE+SREV; | |
268 | ts.ts_cmd.c_loba = blkno - dbtofsb(bp->b_blkno); | |
269 | } | |
270 | } | |
271 | tsaddr->tsdb = ts_uba; | |
272 | return; | |
273 | ||
274 | abort: | |
275 | bp->b_flags |= B_ERROR; | |
276 | ||
277 | next: | |
278 | tstab.b_active = 0; | |
279 | tstab.b_actf = bp->av_forw; | |
280 | iodone(bp); | |
281 | goto loop; | |
282 | } | |
283 | ||
284 | tsintr() | |
285 | { | |
286 | register struct buf *bp; | |
287 | register struct device *tsaddr = TSADDR; | |
288 | register err, errclass, state; | |
289 | ||
290 | if ((bp = tstab.b_actf)==NULL) | |
291 | return; | |
292 | state = tstab.b_active; | |
293 | tstab.b_active = 0; | |
294 | err = tsaddr->tssr & 016; | |
295 | if ((tsaddr->tssr & SC) == 0) | |
296 | err = 0; | |
297 | errclass = 0; | |
298 | switch (err) { | |
299 | case 014: /* unrecoverable */ | |
300 | case 016: /* fatal */ | |
301 | case 002: /* attention (shouldn't happen) */ | |
302 | case 012: /* "recoverable", but shouldn't happen */ | |
303 | errclass = 2; | |
304 | break; | |
305 | ||
306 | case 0: /* all OK */ | |
307 | break; | |
308 | ||
309 | case 004: /* status alert */ | |
310 | if (ts.ts_sts.xs0&RLS && bp==&rtsbuf) /* short record */ | |
311 | break; | |
312 | if (ts.ts_sts.xs0 & TMK) { /* tape mark */ | |
313 | ts.ts_sts.rbpcr = bp->b_bcount; | |
314 | break; | |
315 | } | |
316 | errclass = 1; | |
317 | break; | |
318 | ||
319 | case 010: /* recoverable, tape moved */ | |
320 | if (state==SIO && ++bp->b_errcnt < 10) { | |
321 | ts.ts_cmd.c_cmd |= 01000; /* redo bit */ | |
322 | tstab.b_active = SIO; | |
323 | tsaddr->tsdb = ts_uba; | |
324 | return; | |
325 | } | |
326 | errclass = 1; | |
327 | break; | |
328 | ||
329 | case 006: /* Function reject */ | |
330 | if (state==SIO && ts.ts_sts.xs0 & WLE) | |
331 | printf("Tape needs a ring\n"); | |
332 | if ((ts.ts_sts.xs0&ONL) == 0) /* tape offline */ | |
333 | printf("Tape offline\n"); | |
334 | errclass = 2; | |
335 | } | |
336 | if (errclass) | |
337 | printf("tp: %o %o %o %o %o %o %o %o\n", tsaddr->tssr, | |
338 | ts.ts_sts.s_sts, ts.ts_sts.len, ts.ts_sts.rbpcr, | |
339 | ts.ts_sts.xs0, ts.ts_sts.xs1, ts.ts_sts.xs2, ts.ts_sts.xs3); | |
340 | switch(state) { | |
341 | case SIO: | |
342 | ts_blkno++; | |
b28deaf8 | 343 | ubarelse(&ts_ubinfo); |
9bad5ea5 BJ |
344 | case SCOM: |
345 | tstab.b_errcnt = 0; | |
346 | tstab.b_actf = bp->av_forw; | |
347 | bp->b_resid = ts.ts_sts.rbpcr; | |
348 | iodone(bp); | |
349 | break; | |
350 | ||
351 | case SSFOR: | |
352 | case SSREV: | |
353 | ts_blkno = dbtofsb(bp->b_blkno); | |
354 | break; | |
355 | ||
356 | default: | |
357 | printf("Unknown tape interrupt\n"); | |
358 | errclass = 2; | |
359 | break; | |
360 | } | |
361 | if (errclass > 1) { | |
362 | while (bp = tstab.b_actf) { | |
363 | bp->b_flags |= B_ERROR; | |
364 | iodone(bp); | |
365 | tstab.b_actf = bp->av_forw; | |
366 | } | |
367 | } | |
368 | tsstart(); | |
369 | } | |
370 | ||
371 | tsread(dev) | |
372 | { | |
373 | tsphys(dev); | |
374 | physio(tsstrategy, &rtsbuf, dev, B_READ, minphys); | |
375 | } | |
376 | ||
377 | tswrite(dev) | |
378 | { | |
379 | tsphys(dev); | |
380 | physio(tsstrategy, &rtsbuf, dev, B_WRITE, minphys); | |
381 | } | |
382 | ||
383 | tsphys(dev) | |
384 | { | |
385 | register unit; | |
386 | daddr_t a; | |
387 | ||
388 | a = u.u_offset >> 9; | |
389 | ts_blkno = dbtofsb(a); | |
390 | ts_nxrec = dbtofsb(a)+1; | |
391 | } | |
f0a3ddbd BJ |
392 | |
393 | #define UBMAP (int *)0xf30800 | |
394 | ||
395 | int dtsinfo; | |
e1b765e1 | 396 | struct tsmesg dts; |
f0a3ddbd BJ |
397 | |
398 | twall(start, num) | |
399 | { | |
400 | register struct device *tsaddr = TSPHYS; | |
401 | register int *ubap = UBMAP; | |
402 | register int p, i; | |
403 | ||
404 | tsinit(); | |
405 | /* dump mem */ | |
406 | p = PG_V; | |
407 | i = 0; | |
408 | while (i<num) { | |
409 | *(ubap) = p|i++; | |
410 | *(ubap+1) = p|i; | |
411 | dts.ts_cmd.c_loba = 0; | |
412 | dts.ts_cmd.c_hiba = 0; | |
413 | dts.ts_cmd.c_size = NBPG; | |
414 | dts.ts_cmd.c_cmd = ACK+CVC+WRITE; | |
415 | tsaddr->tsdb = dtsinfo; | |
416 | twait(); | |
417 | } | |
418 | printf("done\n"); | |
419 | } | |
420 | ||
421 | tsinit() | |
422 | { | |
423 | register struct device *tsaddr = TSPHYS; | |
424 | register struct tsmesg *tsm; | |
425 | register int *ubap = UBMAP; | |
426 | register i; | |
427 | ||
428 | tsaddr->tssr = 0; | |
429 | while ((tsaddr->tssr&SSR)==0) | |
430 | ; | |
431 | i = (int)&dts; | |
432 | i &= 0xefffff; | |
433 | dtsinfo = ((i&0777)|02000); | |
434 | tsm = (struct tsmesg *)dtsinfo; | |
435 | i >>= 9; | |
436 | i |= PG_V; | |
437 | *(ubap+2) = i; | |
438 | *(ubap+3) = i+1; | |
439 | dts.ts_cmd.c_cmd = ACK + 04; | |
e1b765e1 | 440 | dts.ts_cmd.c_loba = (int)&tsm->ts_char; |
f0a3ddbd BJ |
441 | dts.ts_cmd.c_hiba = 0; |
442 | dts.ts_cmd.c_size = sizeof(dts.ts_char); | |
e1b765e1 | 443 | dts.ts_char.char_loba = (int)&tsm->ts_sts; |
f0a3ddbd BJ |
444 | dts.ts_char.char_hiba = 0; |
445 | dts.ts_char.char_size = sizeof(dts.ts_sts); | |
446 | dts.ts_char.char_mode = 0400; | |
447 | tsaddr->tsdb = dtsinfo; | |
448 | twait(); | |
449 | } | |
450 | ||
451 | teof() | |
452 | { | |
453 | ||
454 | dtscommand(WTM); | |
455 | } | |
456 | ||
457 | rewind() | |
458 | { | |
459 | ||
460 | dtscommand(REW); | |
461 | } | |
462 | ||
463 | dtscommand(com) | |
464 | { | |
465 | register struct device *tsaddr = TSPHYS; | |
466 | ||
467 | dts.ts_cmd.c_cmd = ACK+CVC+com; | |
468 | dts.ts_cmd.c_loba = 1; | |
469 | tsaddr->tsdb = dtsinfo; | |
470 | twait(); | |
471 | } | |
472 | ||
473 | twait() | |
474 | { | |
475 | register struct device *tsaddr = TSPHYS; | |
476 | register i; | |
477 | ||
478 | while ((tsaddr->tssr&SSR)==0) | |
479 | ; | |
480 | i = tsaddr->tssr; | |
481 | if (i&SC) | |
482 | printf("tssr %x ", i); | |
483 | } | |
9bad5ea5 | 484 | #endif |