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