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