Commit | Line | Data |
---|---|---|
c6306105 C |
1 | /* uu.c 6.1 83/07/29 */ |
2 | ||
3 | #include "uu.h" | |
4 | #if NUU > 0 | |
5 | /* | |
6 | * TU58 DECtape II/DL11 device driver | |
7 | * | |
8 | * The TU58 is treated as a block device (only). Error detection and | |
9 | * recovery is not very extensive, but sufficient to handle the most | |
10 | * common errors. It is assumed that the TU58 will follow the RSP | |
11 | * protocol exactly, very few protocol errors are checked for. | |
12 | * | |
13 | * To reduce interrupt latency, `options UUDMA' should be specified | |
14 | * in the config file to make sure the `pseudo-DMA' code in locore.s | |
15 | * will be compiled into the system. Otherwise overrun errors will | |
16 | * occur frequently (these errors are not reported). | |
17 | * | |
18 | * TODO: | |
19 | * | |
20 | * - Add ioctl code to wind/rewind cassette | |
21 | * | |
22 | */ | |
23 | ||
24 | #include "../machine/pte.h" | |
25 | ||
26 | #include "../h/param.h" | |
27 | #include "../h/systm.h" | |
28 | #include "../h/buf.h" | |
29 | #include "../h/conf.h" | |
30 | #include "../h/time.h" | |
31 | #include "../h/kernel.h" | |
32 | #include "../h/errno.h" | |
33 | #include "../h/file.h" | |
34 | ||
35 | #include "../vax/cpu.h" | |
36 | #include "../vax/nexus.h" | |
37 | #include "../vax/rsp.h" | |
38 | ||
39 | #include "../vaxuba/ubavar.h" | |
40 | #include "../vaxuba/ubareg.h" | |
41 | #include "../vaxuba/uureg.h" | |
42 | ||
43 | #define NTUBLK 512 /* number of blocks on a TU58 cassette */ | |
44 | #define WRV 01 /* bit in minor dev => write w. read verify */ | |
45 | #define NDPC 02 /* drives per controller */ | |
46 | #define NUX NDPC * NUU /* number of drives */ | |
47 | #define NUUQ 02 /* # of block which can be queued up */ | |
48 | #define UMASK 01 /* unit number mask */ | |
49 | #define UUIPL 0x14 /* ipl level to use */ | |
50 | ||
51 | struct packet uucmd[NUU]; /* a command sent to the TU58 */ | |
52 | struct packet uudata[NUU]; /* a command or data returned from TU58 */ | |
53 | struct buf uitab[NUU]; /* buffer queue headers */ | |
54 | ||
55 | /* | |
56 | * Driver soft carrier structure | |
57 | */ | |
58 | struct uu_softc { | |
59 | u_char *tu_rbptr; /* pointer to buffer for read */ | |
60 | int tu_rcnt; /* how much to read */ | |
61 | u_char *tu_wbptr; /* pointer to buffer for write */ | |
62 | int tu_wcnt; /* how much to write */ | |
63 | int tu_state; /* current state of tansfer operation */ | |
64 | int tu_flag; /* read in progress flag */ | |
65 | char *tu_addr; /* real buffer data address */ | |
66 | int tu_count; /* real requested count */ | |
67 | int tu_serrs; /* count of soft errors */ | |
68 | int tu_cerrs; /* count of checksum errors */ | |
69 | int tu_herrs; /* count of hard errors */ | |
70 | char tu_dopen[2]; /* drive is open */ | |
71 | } uu_softc[NUU]; | |
72 | ||
73 | #if defined(VAX750) || defined(VAX730) | |
74 | extern char *tustates[]; | |
75 | #else | |
76 | char *tustates[TUS_NSTATES] = { | |
77 | "INIT1", "INIT2", "IDLE", "SENDH", "SENDD", "SENDC", "SENDR", | |
78 | "SENDW", "GETH", "GETD", "GETC", "GET", "WAIT", "RCVERR", "CHKERR" | |
79 | }; | |
80 | #endif | |
81 | ||
82 | #define UNIT(dev) (minor(dev)>>1) | |
83 | ||
84 | u_char uunull[2] = { 0, 0 }; /* nulls to send for initialization */ | |
85 | u_char uuinit[2] = { TUF_INITF, TUF_INITF }; /* inits to send */ | |
86 | ||
87 | struct uba_device *uudinfo[NUU]; | |
88 | ||
89 | int uuprobe(), uuattach(), uurintr(), uuxintr(), uuwatch(); | |
90 | u_short uustd[] = { 0176500 }; | |
91 | struct uba_driver uudriver = | |
92 | { uuprobe, 0, uuattach, 0, uustd, "uu", uudinfo }; | |
93 | ||
94 | int uuwstart; | |
95 | int uuwake(); | |
96 | static char uu_pcnt[NUX]; /* pee/vee counters, one per drive */ | |
97 | ||
98 | /*ARGSUSED*/ | |
99 | uuprobe(reg) | |
100 | caddr_t reg; | |
101 | { | |
102 | register int br, cvec; /* value result */ | |
103 | struct uudevice *uuaddr = (struct uudevice *)reg; | |
104 | ||
105 | #ifdef lint | |
106 | br = 0; cvec = br; br = cvec; | |
107 | uurintr(0); uuxintr(0); | |
108 | #endif | |
109 | uuaddr->tcs = UUCS_INTR; | |
110 | DELAY(1000); | |
111 | uuaddr->tcs = 0; | |
112 | cvec -= 4; /* since we are using the xmitter intrpt */ | |
113 | return(sizeof (*uuaddr)); | |
114 | } | |
115 | ||
116 | uuattach(ui) | |
117 | register struct uba_device *ui; | |
118 | { | |
119 | } | |
120 | ||
121 | /*ARGSUSED1*/ | |
122 | uuopen(dev, flag) | |
123 | dev_t dev; | |
124 | int flag; | |
125 | { | |
126 | register struct uba_device *ui; | |
127 | register struct uu_softc *uuc; | |
128 | register struct uudevice *uuaddr; | |
129 | int ctlr, unit = UNIT(dev), s; | |
130 | ||
131 | ctlr = unit / NDPC; | |
132 | if (unit >= NUX || (ui = uudinfo[ctlr]) == 0 || ui->ui_alive == 0) | |
133 | return (ENXIO); | |
134 | uuc = &uu_softc[ctlr]; | |
135 | if (uuc->tu_dopen[unit&UMASK]) | |
136 | return (EBUSY); | |
137 | if (uuwstart++ == 0) | |
138 | timeout(uuwatch, (caddr_t)0, hz); | |
139 | ||
140 | uuc->tu_dopen[unit&UMASK]++; | |
141 | uuaddr = (struct uudevice *)ui->ui_addr; | |
142 | s = splx(UUIPL); | |
143 | /* | |
144 | * If the other device on this controller | |
145 | * is already active, no need to initialize | |
146 | */ | |
147 | if (uuc->tu_dopen[0] && uuc->tu_dopen[1]) | |
148 | goto ok; | |
149 | ||
150 | /* | |
151 | * If the unit already initialized, | |
152 | * just enable interrupts and return. | |
153 | */ | |
154 | if (uuc->tu_state == TUS_IDLE) { | |
155 | uuaddr->rcs = UUCS_INTR; | |
156 | goto ok; | |
157 | } | |
158 | ||
159 | /* | |
160 | * Must initialize, reset the cassette | |
161 | * and wait for things to settle down. | |
162 | */ | |
163 | uureset(ctlr); | |
164 | sleep((caddr_t)uuc, PZERO+1); | |
165 | uitab[ctlr].b_active = NULL; | |
166 | if (uuc->tu_state != TUS_IDLE) { | |
167 | uuc->tu_state = TUS_INIT1; | |
168 | uuc->tu_dopen[unit&UMASK] = 0; | |
169 | uuc->tu_rcnt = uuc->tu_wcnt = 0; | |
170 | uuaddr->rcs = 0; | |
171 | uuaddr->tcs = 0; | |
172 | splx(s); | |
173 | return (EIO); | |
174 | } | |
175 | ok: | |
176 | splx(s); | |
177 | return (0); | |
178 | } | |
179 | ||
180 | /* | |
181 | * Wait for all outstanding IO on this drive | |
182 | * complete, before closing. If both drives on | |
183 | * this controller are idle, mark the controller | |
184 | * `inactive'. | |
185 | */ | |
186 | ||
187 | uuclose(dev, flag) | |
188 | dev_t dev; | |
189 | int flag; | |
190 | { | |
191 | int s, unit = UNIT(dev); | |
192 | register struct uu_softc *uuc = &uu_softc[unit/NDPC]; | |
193 | struct buf *bp, *last = NULL; | |
194 | struct uudevice *uuaddr = (struct uudevice *)uudinfo[unit/NDPC]->ui_addr; | |
195 | ||
196 | s = splx(UUIPL); | |
197 | while (uu_pcnt[unit]) | |
198 | sleep(&uu_pcnt[unit], PRIBIO); | |
199 | /* | |
200 | * No more writes are pending, scan the | |
201 | * buffer queue for oustanding reads from | |
202 | * this unit. | |
203 | */ | |
204 | for (bp = uitab[unit/NDPC].b_actf; bp; bp = bp->b_actf) { | |
205 | if (bp->b_dev == dev) | |
206 | last = bp; | |
207 | } | |
208 | if (last) { | |
209 | last->b_flags |= B_CALL; | |
210 | last->b_iodone = uuwake; | |
211 | sleep((caddr_t)last, PRIBIO); | |
212 | } | |
213 | uuc->tu_dopen[unit&UMASK] = 0; | |
214 | if (!uuc->tu_dopen[0] && !uuc->tu_dopen[1]) { | |
215 | uuc->tu_flag = 0; | |
216 | uuaddr->rcs = 0; | |
217 | } | |
218 | splx(s); | |
219 | } | |
220 | ||
221 | uuwake(bp) | |
222 | struct buf *bp; | |
223 | { | |
224 | wakeup(bp); | |
225 | } | |
226 | ||
227 | uureset(ctlr) | |
228 | int ctlr; | |
229 | { | |
230 | register struct uu_softc *uuc = &uu_softc[ctlr]; | |
231 | register struct packet *cmd = &uucmd[ctlr]; | |
232 | struct uba_device *ui = uudinfo[ctlr]; | |
233 | register struct uudevice *uuaddr = (struct uudevice *)ui->ui_addr; | |
234 | ||
235 | uitab[ctlr].b_active++; | |
236 | uuc->tu_state = TUS_INIT1; | |
237 | uuc->tu_wbptr = uunull; | |
238 | uuc->tu_wcnt = sizeof (uunull); | |
239 | uuc->tu_rcnt = 0; | |
240 | cmd->pk_flag = TUF_CMD; | |
241 | cmd->pk_mcount = sizeof (*cmd) - 4; | |
242 | cmd->pk_mod = 0; | |
243 | cmd->pk_seq = 0; | |
244 | cmd->pk_sw = 0; | |
245 | uuaddr->rcs = 0; | |
246 | uuaddr->tcs = UUCS_INTR | UUCS_BREAK; | |
247 | uuxintr(ctlr); /* start output */ | |
248 | } | |
249 | ||
250 | /* | |
251 | * Strategy routine for block I/O | |
252 | */ | |
253 | uustrategy(bp) | |
254 | register struct buf *bp; | |
255 | { | |
256 | register struct buf *uutab; | |
257 | struct uba_device *ui; | |
258 | int s, unit = UNIT(bp->b_dev); | |
259 | ||
260 | if ((unit > NUX) || (bp->b_blkno >= NTUBLK)) | |
261 | goto bad; | |
262 | ui = uudinfo[unit/NDPC]; | |
263 | if (ui == 0 || ui->ui_alive == 0) | |
264 | goto bad; | |
265 | uutab = &uitab[unit/NDPC]; /* one request queue per controller */ | |
266 | s = splx(UUIPL); | |
267 | if ((bp->b_flags&B_READ) == 0) | |
268 | tu_pee(&uu_pcnt[unit]); | |
269 | bp->b_actf = NULL; | |
270 | if (uutab->b_actf == NULL) | |
271 | uutab->b_actf = bp; | |
272 | else | |
273 | uutab->b_actl->b_actf = bp; | |
274 | uutab->b_actl = bp; | |
275 | if (uutab->b_active == 0) | |
276 | uustart(ui); | |
277 | splx(s); | |
278 | return; | |
279 | ||
280 | bad: | |
281 | bp->b_flags |= B_ERROR; | |
282 | bp->b_error = ENXIO; | |
283 | iodone(bp); | |
284 | return; | |
285 | } | |
286 | ||
287 | /* | |
288 | * Start the transfer | |
289 | */ | |
290 | uustart(ui) | |
291 | register struct uba_device *ui; | |
292 | { | |
293 | register struct buf *bp; | |
294 | register struct uu_softc *uuc; | |
295 | struct packet *cmd; | |
296 | int ctlr = ui->ui_unit, s; | |
297 | ||
298 | if ((bp = uitab[ctlr].b_actf) == NULL) | |
299 | return; | |
300 | s = splx(UUIPL); | |
301 | uuc = &uu_softc[ctlr]; | |
302 | if (uuc->tu_state != TUS_IDLE) { | |
303 | uureset(ctlr); | |
304 | splx(s); | |
305 | return; | |
306 | } | |
307 | cmd = &uucmd[ctlr]; | |
308 | uitab[ctlr].b_active++; | |
309 | uitab[ctlr].b_errcnt = 0; | |
310 | uuc->tu_addr = bp->b_un.b_addr; | |
311 | uuc->tu_count = cmd->pk_count = bp->b_bcount; | |
312 | cmd->pk_block = bp->b_blkno; | |
313 | if (bp->b_flags&B_READ) { | |
314 | cmd->pk_op = TUOP_READ; | |
315 | cmd->pk_mod = 0; | |
316 | uuc->tu_state = TUS_SENDR; | |
317 | } else { | |
318 | cmd->pk_op = TUOP_WRITE; | |
319 | cmd->pk_mod = minor(bp->b_dev)&WRV ? TUMD_WRV : 0; | |
320 | uuc->tu_state = TUS_SENDW; | |
321 | } | |
322 | cmd->pk_unit = UNIT(bp->b_dev)&UMASK; | |
323 | cmd->pk_sw = 0; | |
324 | cmd->pk_chksum = | |
325 | tuchk(*((short *)cmd), (u_short *)&cmd->pk_op, (int)cmd->pk_mcount); | |
326 | uuc->tu_wbptr = (u_char *)cmd; | |
327 | uuc->tu_wcnt = sizeof (*cmd); | |
328 | uuxintr(ctlr); | |
329 | splx(s); | |
330 | } | |
331 | ||
332 | /* | |
333 | * TU58 receiver interrupt, handles whatever condition the | |
334 | * pseudo DMA routine in locore is unable to handle, | |
335 | * or, if UUDMA is undefined, handle all receiver interrupt | |
336 | * processing. | |
337 | */ | |
338 | uurintr(ctlr) | |
339 | int ctlr; | |
340 | { | |
341 | struct uba_device *ui = uudinfo[ctlr]; | |
342 | register struct uu_softc *uuc = &uu_softc[ctlr]; | |
343 | register struct uudevice *uuaddr = (struct uudevice *)ui->ui_addr; | |
344 | register struct buf *uutab = &uitab[ctlr]; | |
345 | struct packet *data, *cmd; | |
346 | struct buf *bp; | |
347 | int c, unit; | |
348 | ||
349 | c = uuaddr->rdb; | |
350 | data = &uudata[ctlr]; | |
351 | cmd = &uucmd[ctlr]; | |
352 | #if !defined(UUDMA) | |
353 | if (c & UURDB_ERROR) | |
354 | uuc->tu_state = TUS_RCVERR; | |
355 | else { | |
356 | if (uuc->tu_rcnt) { | |
357 | *uuc->tu_rbptr++ = c; | |
358 | if (--uuc->tu_rcnt) | |
359 | return; | |
360 | } | |
361 | } | |
362 | #endif | |
363 | ||
364 | /* | |
365 | * Switch on the tu_state of the transfer. | |
366 | */ | |
367 | switch(uuc->tu_state) { | |
368 | ||
369 | /* | |
370 | * A data error occured in uudma | |
371 | * (either overrun or break) | |
372 | */ | |
373 | case TUS_RCVERR: | |
374 | if ((c & UURDB_ORUN) == 0) | |
375 | printf("uu%d: break received, transfer restarted\n", | |
376 | data->pk_unit); | |
377 | #ifdef UUDEBUG | |
378 | else | |
379 | printf("uu%d: data overrun, recovered\n", | |
380 | data->pk_unit); | |
381 | #endif | |
382 | uuc->tu_serrs++; | |
383 | uu_restart(ctlr, ui); | |
384 | break; | |
385 | ||
386 | /* | |
387 | * If we get an unexpected "continue", | |
388 | * start all over again... | |
389 | */ | |
390 | case TUS_INIT2: | |
391 | uuc->tu_state = c == TUF_CONT ? TUS_IDLE : TUS_INIT1; | |
392 | uuc->tu_flag = 0; | |
393 | wakeup((caddr_t)uuc); | |
394 | uustart(ui); | |
395 | break; | |
396 | ||
397 | /* | |
398 | * Only transition from this state | |
399 | * is on a "continue", so if we don't | |
400 | * get it, reset the world. | |
401 | */ | |
402 | case TUS_WAIT: /* waiting for continue */ | |
403 | switch(c) { | |
404 | case TUF_CONT: /* got the expected continue */ | |
405 | uuc->tu_flag = 0; | |
406 | data->pk_flag = TUF_DATA; | |
407 | data->pk_mcount = MIN(128, uuc->tu_count); | |
408 | data->pk_chksum = | |
409 | tuchk(*((short *)data), (caddr_t)uuc->tu_addr, | |
410 | (int)data->pk_mcount); | |
411 | uuc->tu_state = TUS_SENDH; | |
412 | uuc->tu_wbptr = (u_char *)data; | |
413 | uuc->tu_wcnt = 2; | |
414 | uuxintr(ctlr); | |
415 | break; | |
416 | ||
417 | case TUF_CMD: /* sending us an END packet...error */ | |
418 | uuc->tu_state = TUS_GET; | |
419 | uuc->tu_rbptr = (u_char *)data; | |
420 | uuc->tu_rcnt = sizeof (*data) - 1; | |
421 | uuc->tu_flag = 1; | |
422 | uuaddr->tcs = 0; | |
423 | *uuc->tu_rbptr++ = c & UUDB_DMASK; | |
424 | break; | |
425 | ||
426 | case TUF_INITF: | |
427 | uureset(ctlr); | |
428 | break; | |
429 | ||
430 | default: /* something random...bad news */ | |
431 | uuc->tu_state = TUS_INIT1; | |
432 | break; | |
433 | } | |
434 | break; | |
435 | ||
436 | case TUS_SENDW: | |
437 | if (c != TUF_CONT && c != TUF_INITF) | |
438 | goto bad; | |
439 | uu_restart(ctlr, ui); | |
440 | break; | |
441 | ||
442 | /* | |
443 | * Got header, now get data; amount to | |
444 | * fetch is included in packet. | |
445 | * (data packets are handled entirely | |
446 | * in uudma) | |
447 | */ | |
448 | case TUS_GETH: | |
449 | #ifndef UUDMA | |
450 | if (data->pk_flag == TUF_DATA) | |
451 | uuc->tu_rbptr = (u_char *)uuc->tu_addr; | |
452 | #endif | |
453 | uuc->tu_rcnt = data->pk_mcount; | |
454 | uuc->tu_state = TUS_GETD; | |
455 | break; | |
456 | ||
457 | /* | |
458 | * Got the data, now fetch the checksum. | |
459 | */ | |
460 | case TUS_GETD: | |
461 | uuc->tu_rbptr = (u_char *)&data->pk_chksum; | |
462 | uuc->tu_rcnt = sizeof (data->pk_chksum); | |
463 | uuc->tu_state = TUS_GETC; | |
464 | break; | |
465 | ||
466 | case TUS_GETC: | |
467 | /* got entire packet */ | |
468 | if (data->pk_chksum != | |
469 | tuchk(*((short *)data), (u_short *) | |
470 | (data->pk_flag == TUF_DATA ? | |
471 | (u_short *) uuc->tu_addr : (u_short *)&data->pk_op), | |
472 | (int)data->pk_mcount)) | |
473 | case TUS_CHKERR: | |
474 | uuc->tu_cerrs++; | |
475 | case TUS_GET: | |
476 | if (data->pk_flag == TUF_DATA) { | |
477 | /* data packet, advance to next */ | |
478 | uuc->tu_addr += data->pk_mcount; | |
479 | uuc->tu_count -= data->pk_mcount; | |
480 | uuc->tu_state = TUS_GETH; | |
481 | uuc->tu_rbptr = (u_char *)data; /* next packet */ | |
482 | uuc->tu_rcnt = 2; | |
483 | } else if (data->pk_flag==TUF_CMD && data->pk_op==TUOP_END) { | |
484 | /* end packet, idle and reenable transmitter */ | |
485 | uuc->tu_state = TUS_IDLE; | |
486 | uuc->tu_flag = 0; | |
487 | uuaddr->tcs = UUCS_INTR; | |
488 | if ((bp = uutab->b_actf) == NULL) { | |
489 | printf("uu%d: no bp, active %d\n", | |
490 | data->pk_unit, uitab[ctlr].b_active); | |
491 | uustart(ui); | |
492 | return; | |
493 | } | |
494 | unit = UNIT(bp->b_dev); | |
495 | if (data->pk_mod > 1) { /* hard error */ | |
496 | printf("uu%d: hard error bn%d,", unit, | |
497 | bp->b_blkno); | |
498 | printf(" pk_mod 0%o\n", data->pk_mod&0xff); | |
499 | bp->b_flags |= B_ERROR; | |
500 | uuc->tu_herrs++; | |
501 | } else if (data->pk_mod) /* soft error */ | |
502 | uuc->tu_serrs++; | |
503 | uutab->b_active = NULL; | |
504 | uutab->b_actf = bp->b_actf; | |
505 | bp->b_resid = uuc->tu_count; | |
506 | if ((bp->b_flags&B_READ) == 0) | |
507 | tu_vee(&uu_pcnt[unit]); | |
508 | iodone(bp); | |
509 | uustart(ui); | |
510 | } else { | |
511 | /* | |
512 | * Neither data nor end: data was lost | |
513 | * somehow, flush and restart the transfer. | |
514 | */ | |
515 | uuaddr->rcs = 0; | |
516 | uu_restart(ctlr, ui); | |
517 | uuc->tu_serrs++; | |
518 | } | |
519 | break; | |
520 | ||
521 | case TUS_IDLE: | |
522 | case TUS_INIT1: | |
523 | break; | |
524 | ||
525 | default: | |
526 | bad: | |
527 | if (c == TUF_INITF) { | |
528 | printf("uu%d protocol error, state=", data->pk_unit); | |
529 | printstate(uuc->tu_state); | |
530 | printf(", op=%x, cnt=%d, block=%d\n", | |
531 | cmd->pk_op, cmd->pk_count, cmd->pk_block); | |
532 | uutab->b_active = NULL; | |
533 | if (bp = uutab->b_actf) { | |
534 | bp->b_flags |= B_ERROR; | |
535 | uutab->b_actf = bp->b_actf; | |
536 | if ((bp->b_flags&B_READ) == 0) | |
537 | tu_vee(&uu_pcnt[unit]); | |
538 | iodone(bp); | |
539 | } | |
540 | uuc->tu_state = TUS_INIT1; | |
541 | } else { | |
542 | printf("uu%d receive state error, state=", | |
543 | data->pk_unit); | |
544 | printstate(uuc->tu_state); | |
545 | printf(", byte=%x\n", c & 0xff); | |
546 | #ifdef notdef | |
547 | uuc->tu_state = TUS_INIT1; | |
548 | #endif | |
549 | wakeup((caddr_t)uuc); | |
550 | } | |
551 | } | |
552 | } | |
553 | ||
554 | ||
555 | /* | |
556 | * TU58 transmitter interrupt | |
557 | */ | |
558 | uuxintr(ctlr) | |
559 | int ctlr; | |
560 | { | |
561 | register struct uu_softc *uuc = &uu_softc[ctlr]; | |
562 | register struct uudevice *uuaddr; | |
563 | register struct packet *data; | |
564 | struct uba_device *ui = uudinfo[ctlr]; | |
565 | int c; | |
566 | ||
567 | data = &uudata[ctlr]; | |
568 | uuaddr = (struct uudevice *) ui->ui_addr; | |
569 | top: | |
570 | if (uuc->tu_wcnt > 0) { | |
571 | /* still stuff to send, send one byte */ | |
572 | while ((uuaddr->tcs & UUCS_READY) == 0) | |
573 | ; | |
574 | uuaddr->tdb = *uuc->tu_wbptr++; | |
575 | uuc->tu_wcnt--; | |
576 | return; | |
577 | } | |
578 | ||
579 | /* | |
580 | * Last message byte was sent out. | |
581 | * Switch on tu_state of transfer. | |
582 | */ | |
583 | switch(uuc->tu_state) { | |
584 | ||
585 | /* | |
586 | * Two nulls have been sent, remove break, and send inits | |
587 | */ | |
588 | case TUS_INIT1: | |
589 | uuc->tu_flag = 0; | |
590 | uuaddr->tcs = UUCS_INTR; | |
591 | uuc->tu_state = TUS_INIT2; | |
592 | uuc->tu_wbptr = uuinit; | |
593 | uuc->tu_wcnt = sizeof (uuinit); | |
594 | goto top; | |
595 | ||
596 | /* | |
597 | * Inits have been sent, wait for a continue msg. | |
598 | */ | |
599 | case TUS_INIT2: | |
600 | c = uuaddr->rdb; /* prevent overrun error */ | |
601 | uuaddr->rcs = UUCS_INTR; | |
602 | uuc->tu_flag = 1; | |
603 | break; | |
604 | ||
605 | /* | |
606 | * Read cmd packet sent, get ready for data | |
607 | */ | |
608 | case TUS_SENDR: | |
609 | uuc->tu_state = TUS_GETH; | |
610 | uuc->tu_rbptr = (u_char *)data; | |
611 | uuc->tu_rcnt = 2; | |
612 | uuc->tu_flag = 1; | |
613 | uuaddr->tcs = 0; | |
614 | uuaddr->rcs = UUCS_INTR; | |
615 | break; | |
616 | ||
617 | /* | |
618 | * Write cmd packet sent, wait for continue | |
619 | */ | |
620 | case TUS_SENDW: | |
621 | uuc->tu_state = TUS_WAIT; | |
622 | uuc->tu_flag = 1; | |
623 | if ((uuaddr->rcs&UUCS_INTR) == 0) { | |
624 | printf("NO IE\n"); | |
625 | uuaddr->rcs = UUCS_INTR; | |
626 | } | |
627 | break; | |
628 | ||
629 | /* | |
630 | * Header sent, send data. | |
631 | */ | |
632 | case TUS_SENDH: | |
633 | uuc->tu_state = TUS_SENDD; | |
634 | uuc->tu_wbptr = (u_char *)uuc->tu_addr; | |
635 | uuc->tu_wcnt = data->pk_mcount; | |
636 | goto top; | |
637 | ||
638 | /* | |
639 | * Data sent, follow with checksum. | |
640 | */ | |
641 | case TUS_SENDD: | |
642 | uuc->tu_state = TUS_SENDC; | |
643 | uuc->tu_wbptr = (u_char *)&data->pk_chksum; | |
644 | uuc->tu_wcnt = 2; | |
645 | goto top; | |
646 | ||
647 | /* | |
648 | * Checksum sent, wait for continue. | |
649 | */ | |
650 | case TUS_SENDC: | |
651 | /* | |
652 | * Update buffer address and count. | |
653 | */ | |
654 | uuc->tu_addr += data->pk_mcount; | |
655 | uuc->tu_count -= data->pk_mcount; | |
656 | if (uuc->tu_count > 0) { | |
657 | uuc->tu_state = TUS_WAIT; | |
658 | uuc->tu_flag = 1; | |
659 | break; | |
660 | } | |
661 | ||
662 | /* | |
663 | * End of transmission, get ready for end packet. | |
664 | */ | |
665 | uuc->tu_state = TUS_GET; | |
666 | uuc->tu_rbptr = (u_char *)data; | |
667 | uuc->tu_rcnt = sizeof (*data); | |
668 | uuc->tu_flag = 1; | |
669 | uuaddr->tcs = 0; | |
670 | break; | |
671 | ||
672 | /* | |
673 | * Random interrupt | |
674 | */ | |
675 | case TUS_IDLE: /* stray interrupt? */ | |
676 | ||
677 | default: | |
678 | break; | |
679 | } | |
680 | } | |
681 | ||
682 | uuwatch() | |
683 | { | |
684 | register struct uu_softc *uuc; | |
685 | register struct uudevice *uuaddr; | |
686 | struct uba_device *ui; | |
687 | struct buf *bp, *uutab; | |
688 | int s, ctlr, active = 0; | |
689 | ||
690 | for (ctlr=0; ctlr<NUU; ctlr++) { | |
691 | int i; | |
692 | ||
693 | uuc = &uu_softc[ctlr]; | |
694 | ||
695 | if (uuc->tu_dopen[0] || uuc->tu_dopen[1]) | |
696 | active++; | |
697 | if (uuc->tu_flag == 0) | |
698 | /* | |
699 | * If no read is in progress | |
700 | * just skip | |
701 | */ | |
702 | continue; | |
703 | ||
704 | ui = uudinfo[ctlr]; | |
705 | uuaddr = (struct uudevice *)ui->ui_addr; | |
706 | uutab = &uitab[ctlr]; | |
707 | if (uuc->tu_flag++ < 40) | |
708 | continue; | |
709 | printf("uu%d: read stalled\n", uudata[ctlr].pk_unit); | |
710 | #ifdef UUDEBUG | |
711 | printf("%X %X %X %X %X %X %X\n", uuc->tu_rbptr, uuc->tu_rcnt, | |
712 | uuc->tu_wbptr, uuc->tu_wcnt, uuc->tu_state, uuc->tu_addr, | |
713 | uuc->tu_count); | |
714 | #endif | |
715 | s = splx(UUIPL); | |
716 | uuc->tu_flag = 0; | |
717 | i = uuaddr->rdb; /* dummy */ | |
718 | uuaddr->rcs = UUCS_INTR; /* in case we were flushing */ | |
719 | uuaddr->tcs = UUCS_INTR; | |
720 | uuc->tu_state = TUS_IDLE; | |
721 | if (!uutab->b_active) { | |
722 | wakeup((caddr_t)uuc); | |
723 | goto retry; | |
724 | } | |
725 | if (++uutab->b_errcnt <= 1) { | |
726 | uustart(ui); | |
727 | goto retry; | |
728 | } | |
729 | if (bp = uutab->b_actf) { | |
730 | bp->b_flags |= B_ERROR; | |
731 | if ((bp->b_flags&B_READ) == 0) | |
732 | tu_vee(&uu_pcnt[UNIT(bp->b_dev)]); | |
733 | iodone(bp); | |
734 | } | |
735 | retry: | |
736 | (void) splx(s); | |
737 | } | |
738 | if (active) | |
739 | timeout(uuwatch, (caddr_t)0, hz); | |
740 | else | |
741 | uuwstart = 0; | |
742 | return; | |
743 | } | |
744 | ||
745 | #if !defined(VAX750) && !defined(VAX730) | |
746 | /* | |
747 | * Compute checksum TU58 fashion | |
748 | */ | |
749 | #ifdef lint | |
750 | tuchk(word, cp, n) | |
751 | register word; | |
752 | register unsigned short *cp; | |
753 | int n; | |
754 | { | |
755 | register int c = n >> 1; | |
756 | register long temp; | |
757 | ||
758 | do { | |
759 | temp = *cp++; /* temp, only because vax cc won't *r++ */ | |
760 | word += temp; | |
761 | } while (--c > 0); | |
762 | if (n & 1) | |
763 | word += *(unsigned char *)cp; | |
764 | while (word & 0xffff0000) | |
765 | word = (word & 0xffff) + ((word >> 16) & 0xffff); | |
766 | return (word); | |
767 | } | |
768 | #else | |
769 | tuchk(word0, wp, n) | |
770 | register int word0; /* r11 */ | |
771 | register char *wp; /* r10 */ | |
772 | register int n; /* r9 */ | |
773 | { | |
774 | asm("loop:"); | |
775 | asm(" addw2 (r10)+,r11"); /* add a word to sum */ | |
776 | asm(" adwc $0,r11"); /* add in carry, end-around */ | |
777 | asm(" acbl $2,$-2,r9,loop"); /* done yet? */ | |
778 | asm(" blbc r9,ok"); /* odd byte count? */ | |
779 | asm(" movzbw (r10),r10"); /* yes, get last byte */ | |
780 | asm(" addw2 r10,r11"); /* add it in */ | |
781 | asm(" adwc $0,r11"); /* and the carry */ | |
782 | asm("ok:"); | |
783 | asm(" movl r11,r0"); /* return sum */ | |
784 | } | |
785 | #endif | |
786 | ||
787 | /* | |
788 | * Make sure this incredibly slow device | |
789 | * doesn't eat up all the buffers in the | |
790 | * system by putting the requesting process | |
791 | * (remember: this device is 'single-user') | |
792 | * to sleep if the write-behind queue grows | |
793 | * larger than NUUQ. | |
794 | */ | |
795 | tu_pee(cp) | |
796 | char *cp; | |
797 | { | |
798 | register int s; | |
799 | ||
800 | s = splx(UUIPL); | |
801 | if (++(*cp) > NUUQ) | |
802 | sleep(cp, PRIBIO); | |
803 | splx(s); | |
804 | } | |
805 | ||
806 | tu_vee(cp) | |
807 | char *cp; | |
808 | { | |
809 | register int s; | |
810 | ||
811 | s = splx(UUIPL); | |
812 | if (--(*cp) <= NUUQ) | |
813 | wakeup(cp); | |
814 | splx(s); | |
815 | } | |
816 | #endif | |
817 | ||
818 | uuioctl(dev, cmd, data, flag) | |
819 | dev_t dev; | |
820 | caddr_t data; | |
821 | { | |
822 | /* | |
823 | * add code to wind/rewind cassette here | |
824 | */ | |
825 | return (ENXIO); | |
826 | } | |
827 | ||
828 | uu_restart(ctlr, ui) | |
829 | int ctlr; | |
830 | struct uba_device *ui; | |
831 | { | |
832 | uureset(ctlr); | |
833 | timeout(uustart, (caddr_t)ui, hz * 3); | |
834 | } | |
835 | ||
836 | #endif |