| 1 | /* |
| 2 | * Copyright (c) 1982, 1986 Regents of the University of California. |
| 3 | * All rights reserved. The Berkeley software License Agreement |
| 4 | * specifies the terms and conditions for redistribution. |
| 5 | * |
| 6 | * @(#)uu.c 7.5 (Berkeley) %G% |
| 7 | */ |
| 8 | |
| 9 | #include "uu.h" |
| 10 | #if NUU > 0 |
| 11 | /* |
| 12 | * TU58 DECtape II/DL11 device driver |
| 13 | * |
| 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 |
| 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 | * |
| 28 | */ |
| 29 | |
| 30 | #include "../include/pte.h" |
| 31 | |
| 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" |
| 40 | |
| 41 | #include "../include/cpu.h" |
| 42 | #include "../vax/nexus.h" |
| 43 | #include "../vax/rsp.h" |
| 44 | |
| 45 | #include "ubavar.h" |
| 46 | #include "ubareg.h" |
| 47 | #include "uureg.h" |
| 48 | |
| 49 | #define NTUBLK 512 /* number of blocks on a TU58 cassette */ |
| 50 | #define WRV 01 /* bit in minor dev => write w. read verify */ |
| 51 | #define NDPC 02 /* drives per controller */ |
| 52 | #define NUX NDPC * NUU /* number of drives */ |
| 53 | #define NUUQ 02 /* # of block which can be queued up */ |
| 54 | #define UMASK 01 /* unit number mask */ |
| 55 | #define UUIPL 0x14 /* ipl level to use */ |
| 56 | |
| 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 */ |
| 60 | |
| 61 | /* |
| 62 | * Driver soft carrier structure |
| 63 | */ |
| 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 */ |
| 77 | } uu_softc[NUU]; |
| 78 | |
| 79 | #if defined(VAX750) || defined(VAX730) |
| 80 | extern char *tustates[]; |
| 81 | #else |
| 82 | char *tustates[TUS_NSTATES] = { |
| 83 | "INIT1", "INIT2", "IDLE", "SENDH", "SENDD", "SENDC", "SENDR", |
| 84 | "SENDW", "GETH", "GETD", "GETC", "GET", "WAIT", "RCVERR", "CHKERR" |
| 85 | }; |
| 86 | #endif |
| 87 | |
| 88 | #define UNIT(dev) (minor(dev)>>1) |
| 89 | |
| 90 | u_char uunull[2] = { 0, 0 }; /* nulls to send for initialization */ |
| 91 | u_char uuinit[2] = { TUF_INITF, TUF_INITF }; /* inits to send */ |
| 92 | |
| 93 | struct uba_device *uudinfo[NUU]; |
| 94 | |
| 95 | int uuprobe(), uuattach(), uurintr(), uuxintr(), uuwatch(); |
| 96 | u_short uustd[] = { 0176500, 0 }; |
| 97 | struct uba_driver uudriver = |
| 98 | { uuprobe, 0, uuattach, 0, uustd, "uu", uudinfo }; |
| 99 | |
| 100 | int uuwstart; |
| 101 | int uuwake(); |
| 102 | static char uu_pcnt[NUX]; /* pee/vee counters, one per drive */ |
| 103 | |
| 104 | /*ARGSUSED*/ |
| 105 | uuprobe(reg) |
| 106 | caddr_t reg; |
| 107 | { |
| 108 | register int br, cvec; /* value result */ |
| 109 | struct uudevice *uuaddr = (struct uudevice *)reg; |
| 110 | |
| 111 | #ifdef lint |
| 112 | br = 0; cvec = br; br = cvec; |
| 113 | uurintr(0); uuxintr(0); |
| 114 | #endif |
| 115 | uuaddr->tcs = UUCS_INTR; |
| 116 | DELAY(1000); |
| 117 | uuaddr->tcs = 0; |
| 118 | cvec -= 4; /* since we are using the xmitter intrpt */ |
| 119 | return(sizeof (*uuaddr)); |
| 120 | } |
| 121 | |
| 122 | uuattach(ui) |
| 123 | register struct uba_device *ui; |
| 124 | { |
| 125 | } |
| 126 | |
| 127 | /*ARGSUSED1*/ |
| 128 | uuopen(dev, flag) |
| 129 | dev_t dev; |
| 130 | int flag; |
| 131 | { |
| 132 | register struct uba_device *ui; |
| 133 | register struct uu_softc *uuc; |
| 134 | register struct uudevice *uuaddr; |
| 135 | int ctlr, unit = UNIT(dev), s, error; |
| 136 | |
| 137 | ctlr = unit / NDPC; |
| 138 | if (unit >= NUX || (ui = uudinfo[ctlr]) == 0 || ui->ui_alive == 0) |
| 139 | return (ENXIO); |
| 140 | uuc = &uu_softc[ctlr]; |
| 141 | if (uuc->tu_dopen[unit&UMASK]) |
| 142 | return (EBUSY); |
| 143 | if (uuwstart++ == 0) |
| 144 | timeout(uuwatch, (caddr_t)0, hz); |
| 145 | |
| 146 | uuc->tu_dopen[unit&UMASK]++; |
| 147 | uuaddr = (struct uudevice *)ui->ui_addr; |
| 148 | s = splx(UUIPL); |
| 149 | /* |
| 150 | * If the other device on this controller |
| 151 | * is already active, no need to initialize |
| 152 | */ |
| 153 | if (uuc->tu_dopen[0] && uuc->tu_dopen[1]) |
| 154 | goto ok; |
| 155 | |
| 156 | /* |
| 157 | * If the unit already initialized, |
| 158 | * just enable interrupts and return. |
| 159 | */ |
| 160 | if (uuc->tu_state == TUS_IDLE) { |
| 161 | uuaddr->rcs = UUCS_INTR; |
| 162 | goto ok; |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | * Must initialize, reset the cassette |
| 167 | * and wait for things to settle down. |
| 168 | */ |
| 169 | uureset(ctlr); |
| 170 | if (error = tsleep((caddr_t)uuc, (PZERO+1) | PCATCH, devopn, 0)) { |
| 171 | splx(s); |
| 172 | return (error); |
| 173 | } |
| 174 | uitab[ctlr].b_active = NULL; |
| 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; |
| 179 | uuaddr->rcs = 0; |
| 180 | uuaddr->tcs = 0; |
| 181 | splx(s); |
| 182 | return (EIO); |
| 183 | } |
| 184 | ok: |
| 185 | splx(s); |
| 186 | return (0); |
| 187 | } |
| 188 | |
| 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 | |
| 196 | uuclose(dev, flag) |
| 197 | dev_t dev; |
| 198 | int flag; |
| 199 | { |
| 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; |
| 204 | |
| 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 | */ |
| 213 | for (bp = uitab[unit/NDPC].b_actf; bp; bp = bp->b_actf) { |
| 214 | if (bp->b_dev == dev) |
| 215 | last = bp; |
| 216 | } |
| 217 | if (last) { |
| 218 | last->b_flags |= B_CALL; |
| 219 | last->b_iodone = uuwake; |
| 220 | sleep((caddr_t)last, PRIBIO); |
| 221 | } |
| 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; |
| 226 | } |
| 227 | splx(s); |
| 228 | return (0); |
| 229 | } |
| 230 | |
| 231 | uuwake(bp) |
| 232 | struct buf *bp; |
| 233 | { |
| 234 | wakeup(bp); |
| 235 | } |
| 236 | |
| 237 | uureset(ctlr) |
| 238 | int ctlr; |
| 239 | { |
| 240 | register struct uu_softc *uuc = &uu_softc[ctlr]; |
| 241 | register struct packet *cmd = &uucmd[ctlr]; |
| 242 | struct uba_device *ui = uudinfo[ctlr]; |
| 243 | register struct uudevice *uuaddr = (struct uudevice *)ui->ui_addr; |
| 244 | |
| 245 | uitab[ctlr].b_active++; |
| 246 | uuc->tu_state = TUS_INIT1; |
| 247 | uuc->tu_wbptr = uunull; |
| 248 | uuc->tu_wcnt = sizeof (uunull); |
| 249 | uuc->tu_rcnt = 0; |
| 250 | cmd->pk_flag = TUF_CMD; |
| 251 | cmd->pk_mcount = sizeof (*cmd) - 4; |
| 252 | cmd->pk_mod = 0; |
| 253 | cmd->pk_seq = 0; |
| 254 | cmd->pk_sw = 0; |
| 255 | uuaddr->rcs = 0; |
| 256 | uuaddr->tcs = UUCS_INTR | UUCS_BREAK; |
| 257 | uuxintr(ctlr); /* start output */ |
| 258 | } |
| 259 | |
| 260 | /* |
| 261 | * Strategy routine for block I/O |
| 262 | */ |
| 263 | uustrategy(bp) |
| 264 | register struct buf *bp; |
| 265 | { |
| 266 | register struct buf *uutab; |
| 267 | struct uba_device *ui; |
| 268 | int s, unit = UNIT(bp->b_dev); |
| 269 | |
| 270 | if ((unit > NUX) || (bp->b_blkno >= NTUBLK)) |
| 271 | goto bad; |
| 272 | ui = uudinfo[unit/NDPC]; |
| 273 | if (ui == 0 || ui->ui_alive == 0) |
| 274 | goto bad; |
| 275 | uutab = &uitab[unit/NDPC]; /* one request queue per controller */ |
| 276 | s = splx(UUIPL); |
| 277 | if ((bp->b_flags&B_READ) == 0) |
| 278 | tu_pee(&uu_pcnt[unit]); |
| 279 | bp->b_actf = NULL; |
| 280 | if (uutab->b_actf == NULL) |
| 281 | uutab->b_actf = bp; |
| 282 | else |
| 283 | uutab->b_actl->b_actf = bp; |
| 284 | uutab->b_actl = bp; |
| 285 | if (uutab->b_active == 0) |
| 286 | uustart(ui); |
| 287 | splx(s); |
| 288 | return; |
| 289 | |
| 290 | bad: |
| 291 | bp->b_flags |= B_ERROR; |
| 292 | bp->b_error = ENXIO; |
| 293 | iodone(bp); |
| 294 | return; |
| 295 | } |
| 296 | |
| 297 | /* |
| 298 | * Start the transfer |
| 299 | */ |
| 300 | uustart(ui) |
| 301 | register struct uba_device *ui; |
| 302 | { |
| 303 | register struct buf *bp; |
| 304 | register struct uu_softc *uuc; |
| 305 | struct packet *cmd; |
| 306 | int ctlr = ui->ui_unit, s; |
| 307 | |
| 308 | if ((bp = uitab[ctlr].b_actf) == NULL) |
| 309 | return; |
| 310 | s = splx(UUIPL); |
| 311 | uuc = &uu_softc[ctlr]; |
| 312 | if (uuc->tu_state != TUS_IDLE) { |
| 313 | uureset(ctlr); |
| 314 | splx(s); |
| 315 | return; |
| 316 | } |
| 317 | cmd = &uucmd[ctlr]; |
| 318 | uitab[ctlr].b_active++; |
| 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 | } |
| 332 | cmd->pk_unit = UNIT(bp->b_dev)&UMASK; |
| 333 | cmd->pk_sw = 0; |
| 334 | cmd->pk_chksum = |
| 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); |
| 338 | uuxintr(ctlr); |
| 339 | splx(s); |
| 340 | } |
| 341 | |
| 342 | /* |
| 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. |
| 347 | */ |
| 348 | uurintr(ctlr) |
| 349 | int ctlr; |
| 350 | { |
| 351 | struct uba_device *ui = uudinfo[ctlr]; |
| 352 | register struct uu_softc *uuc = &uu_softc[ctlr]; |
| 353 | register struct uudevice *uuaddr = (struct uudevice *)ui->ui_addr; |
| 354 | register struct buf *uutab = &uitab[ctlr]; |
| 355 | struct packet *data, *cmd; |
| 356 | struct buf *bp; |
| 357 | int c, unit; |
| 358 | |
| 359 | c = uuaddr->rdb; |
| 360 | data = &uudata[ctlr]; |
| 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: |
| 384 | if ((c & UURDB_ORUN) == 0) |
| 385 | printf("uu%d: break received, transfer restarted\n", |
| 386 | data->pk_unit); |
| 387 | #ifdef UUDEBUG |
| 388 | else |
| 389 | printf("uu%d: data overrun, recovered\n", |
| 390 | data->pk_unit); |
| 391 | #endif |
| 392 | uuc->tu_serrs++; |
| 393 | uu_restart(ctlr, ui); |
| 394 | break; |
| 395 | |
| 396 | /* |
| 397 | * If we get an unexpected "continue", |
| 398 | * start all over again... |
| 399 | */ |
| 400 | case TUS_INIT2: |
| 401 | uuc->tu_state = c == TUF_CONT ? TUS_IDLE : TUS_INIT1; |
| 402 | uuc->tu_flag = 0; |
| 403 | wakeup((caddr_t)uuc); |
| 404 | uustart(ui); |
| 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 | */ |
| 412 | case TUS_WAIT: /* waiting for continue */ |
| 413 | switch(c) { |
| 414 | case TUF_CONT: /* got the expected continue */ |
| 415 | uuc->tu_flag = 0; |
| 416 | data->pk_flag = TUF_DATA; |
| 417 | data->pk_mcount = MIN(128, uuc->tu_count); |
| 418 | data->pk_chksum = |
| 419 | tuchk(*((short *)data), (caddr_t)uuc->tu_addr, |
| 420 | (int)data->pk_mcount); |
| 421 | uuc->tu_state = TUS_SENDH; |
| 422 | uuc->tu_wbptr = (u_char *)data; |
| 423 | uuc->tu_wcnt = 2; |
| 424 | uuxintr(ctlr); |
| 425 | break; |
| 426 | |
| 427 | case TUF_CMD: /* sending us an END packet...error */ |
| 428 | uuc->tu_state = TUS_GET; |
| 429 | uuc->tu_rbptr = (u_char *)data; |
| 430 | uuc->tu_rcnt = sizeof (*data) - 1; |
| 431 | uuc->tu_flag = 1; |
| 432 | uuaddr->tcs = 0; |
| 433 | *uuc->tu_rbptr++ = c & UUDB_DMASK; |
| 434 | break; |
| 435 | |
| 436 | case TUF_INITF: |
| 437 | uureset(ctlr); |
| 438 | break; |
| 439 | |
| 440 | default: /* something random...bad news */ |
| 441 | uuc->tu_state = TUS_INIT1; |
| 442 | break; |
| 443 | } |
| 444 | break; |
| 445 | |
| 446 | case TUS_SENDW: |
| 447 | if (c != TUF_CONT && c != TUF_INITF) |
| 448 | goto bad; |
| 449 | uu_restart(ctlr, ui); |
| 450 | break; |
| 451 | |
| 452 | /* |
| 453 | * Got header, now get data; amount to |
| 454 | * fetch is included in packet. |
| 455 | * (data packets are handled entirely |
| 456 | * in uudma) |
| 457 | */ |
| 458 | case TUS_GETH: |
| 459 | #ifndef UUDMA |
| 460 | if (data->pk_flag == TUF_DATA) |
| 461 | uuc->tu_rbptr = (u_char *)uuc->tu_addr; |
| 462 | #endif |
| 463 | uuc->tu_rcnt = data->pk_mcount; |
| 464 | uuc->tu_state = TUS_GETD; |
| 465 | break; |
| 466 | |
| 467 | /* |
| 468 | * Got the data, now fetch the checksum. |
| 469 | */ |
| 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; |
| 474 | break; |
| 475 | |
| 476 | case TUS_GETC: |
| 477 | /* got entire packet */ |
| 478 | if (data->pk_chksum != |
| 479 | tuchk(*((short *)data), (u_short *) |
| 480 | (data->pk_flag == TUF_DATA ? |
| 481 | (u_short *) uuc->tu_addr : (u_short *)&data->pk_op), |
| 482 | (int)data->pk_mcount)) |
| 483 | case TUS_CHKERR: |
| 484 | uuc->tu_cerrs++; |
| 485 | case TUS_GET: |
| 486 | if (data->pk_flag == TUF_DATA) { |
| 487 | /* data packet, advance to next */ |
| 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; |
| 493 | } else if (data->pk_flag==TUF_CMD && data->pk_op==TUOP_END) { |
| 494 | /* end packet, idle and reenable transmitter */ |
| 495 | uuc->tu_state = TUS_IDLE; |
| 496 | uuc->tu_flag = 0; |
| 497 | uuaddr->tcs = UUCS_INTR; |
| 498 | if ((bp = uutab->b_actf) == NULL) { |
| 499 | printf("uu%d: no bp, active %d\n", |
| 500 | data->pk_unit, uitab[ctlr].b_active); |
| 501 | uustart(ui); |
| 502 | return; |
| 503 | } |
| 504 | unit = UNIT(bp->b_dev); |
| 505 | if (data->pk_mod > 1) { /* hard error */ |
| 506 | printf("uu%d: hard error bn%d,", unit, |
| 507 | bp->b_blkno); |
| 508 | printf(" pk_mod 0%o\n", data->pk_mod&0xff); |
| 509 | bp->b_flags |= B_ERROR; |
| 510 | uuc->tu_herrs++; |
| 511 | } else if (data->pk_mod) /* soft error */ |
| 512 | uuc->tu_serrs++; |
| 513 | uutab->b_active = NULL; |
| 514 | uutab->b_actf = bp->b_actf; |
| 515 | bp->b_resid = uuc->tu_count; |
| 516 | if ((bp->b_flags&B_READ) == 0) |
| 517 | tu_vee(&uu_pcnt[unit]); |
| 518 | iodone(bp); |
| 519 | uustart(ui); |
| 520 | } else { |
| 521 | /* |
| 522 | * Neither data nor end: data was lost |
| 523 | * somehow, flush and restart the transfer. |
| 524 | */ |
| 525 | uuaddr->rcs = 0; |
| 526 | uu_restart(ctlr, ui); |
| 527 | uuc->tu_serrs++; |
| 528 | } |
| 529 | break; |
| 530 | |
| 531 | case TUS_IDLE: |
| 532 | case TUS_INIT1: |
| 533 | break; |
| 534 | |
| 535 | default: |
| 536 | bad: |
| 537 | if (c == TUF_INITF) { |
| 538 | printf("uu%d protocol error, state=", data->pk_unit); |
| 539 | printstate(uuc->tu_state); |
| 540 | printf(", op=%x, cnt=%d, block=%d\n", |
| 541 | cmd->pk_op, cmd->pk_count, cmd->pk_block); |
| 542 | uutab->b_active = NULL; |
| 543 | if (bp = uutab->b_actf) { |
| 544 | bp->b_flags |= B_ERROR; |
| 545 | uutab->b_actf = bp->b_actf; |
| 546 | if ((bp->b_flags&B_READ) == 0) |
| 547 | tu_vee(&uu_pcnt[unit]); |
| 548 | iodone(bp); |
| 549 | } |
| 550 | uuc->tu_state = TUS_INIT1; |
| 551 | } else { |
| 552 | printf("uu%d receive state error, state=", |
| 553 | data->pk_unit); |
| 554 | printstate(uuc->tu_state); |
| 555 | printf(", byte=%x\n", c & 0xff); |
| 556 | #ifdef notdef |
| 557 | uuc->tu_state = TUS_INIT1; |
| 558 | #endif |
| 559 | wakeup((caddr_t)uuc); |
| 560 | } |
| 561 | } |
| 562 | } |
| 563 | |
| 564 | |
| 565 | /* |
| 566 | * TU58 transmitter interrupt |
| 567 | */ |
| 568 | uuxintr(ctlr) |
| 569 | int ctlr; |
| 570 | { |
| 571 | register struct uu_softc *uuc = &uu_softc[ctlr]; |
| 572 | register struct uudevice *uuaddr; |
| 573 | register struct packet *data; |
| 574 | struct uba_device *ui = uudinfo[ctlr]; |
| 575 | int c; |
| 576 | |
| 577 | data = &uudata[ctlr]; |
| 578 | uuaddr = (struct uudevice *) ui->ui_addr; |
| 579 | top: |
| 580 | if (uuc->tu_wcnt > 0) { |
| 581 | /* still stuff to send, send one byte */ |
| 582 | while ((uuaddr->tcs & UUCS_READY) == 0) |
| 583 | ; |
| 584 | uuaddr->tdb = *uuc->tu_wbptr++; |
| 585 | uuc->tu_wcnt--; |
| 586 | return; |
| 587 | } |
| 588 | |
| 589 | /* |
| 590 | * Last message byte was sent out. |
| 591 | * Switch on tu_state of transfer. |
| 592 | */ |
| 593 | switch(uuc->tu_state) { |
| 594 | |
| 595 | /* |
| 596 | * Two nulls have been sent, remove break, and send inits |
| 597 | */ |
| 598 | case TUS_INIT1: |
| 599 | uuc->tu_flag = 0; |
| 600 | uuaddr->tcs = UUCS_INTR; |
| 601 | uuc->tu_state = TUS_INIT2; |
| 602 | uuc->tu_wbptr = uuinit; |
| 603 | uuc->tu_wcnt = sizeof (uuinit); |
| 604 | goto top; |
| 605 | |
| 606 | /* |
| 607 | * Inits have been sent, wait for a continue msg. |
| 608 | */ |
| 609 | case TUS_INIT2: |
| 610 | c = uuaddr->rdb; /* prevent overrun error */ |
| 611 | uuaddr->rcs = UUCS_INTR; |
| 612 | uuc->tu_flag = 1; |
| 613 | break; |
| 614 | |
| 615 | /* |
| 616 | * Read cmd packet sent, get ready for data |
| 617 | */ |
| 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; |
| 623 | uuaddr->tcs = 0; |
| 624 | uuaddr->rcs = UUCS_INTR; |
| 625 | break; |
| 626 | |
| 627 | /* |
| 628 | * Write cmd packet sent, wait for continue |
| 629 | */ |
| 630 | case TUS_SENDW: |
| 631 | uuc->tu_state = TUS_WAIT; |
| 632 | uuc->tu_flag = 1; |
| 633 | if ((uuaddr->rcs&UUCS_INTR) == 0) { |
| 634 | printf("NO IE\n"); |
| 635 | uuaddr->rcs = UUCS_INTR; |
| 636 | } |
| 637 | break; |
| 638 | |
| 639 | /* |
| 640 | * Header sent, send data. |
| 641 | */ |
| 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; |
| 646 | goto top; |
| 647 | |
| 648 | /* |
| 649 | * Data sent, follow with checksum. |
| 650 | */ |
| 651 | case TUS_SENDD: |
| 652 | uuc->tu_state = TUS_SENDC; |
| 653 | uuc->tu_wbptr = (u_char *)&data->pk_chksum; |
| 654 | uuc->tu_wcnt = 2; |
| 655 | goto top; |
| 656 | |
| 657 | /* |
| 658 | * Checksum sent, wait for continue. |
| 659 | */ |
| 660 | case TUS_SENDC: |
| 661 | /* |
| 662 | * Update buffer address and count. |
| 663 | */ |
| 664 | uuc->tu_addr += data->pk_mcount; |
| 665 | uuc->tu_count -= data->pk_mcount; |
| 666 | if (uuc->tu_count > 0) { |
| 667 | uuc->tu_state = TUS_WAIT; |
| 668 | uuc->tu_flag = 1; |
| 669 | break; |
| 670 | } |
| 671 | |
| 672 | /* |
| 673 | * End of transmission, get ready for end packet. |
| 674 | */ |
| 675 | uuc->tu_state = TUS_GET; |
| 676 | uuc->tu_rbptr = (u_char *)data; |
| 677 | uuc->tu_rcnt = sizeof (*data); |
| 678 | uuc->tu_flag = 1; |
| 679 | uuaddr->tcs = 0; |
| 680 | break; |
| 681 | |
| 682 | /* |
| 683 | * Random interrupt |
| 684 | */ |
| 685 | case TUS_IDLE: /* stray interrupt? */ |
| 686 | |
| 687 | default: |
| 688 | break; |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | uuwatch() |
| 693 | { |
| 694 | register struct uu_softc *uuc; |
| 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]; |
| 704 | |
| 705 | if (uuc->tu_dopen[0] || uuc->tu_dopen[1]) |
| 706 | active++; |
| 707 | if (uuc->tu_flag == 0) |
| 708 | /* |
| 709 | * If no read is in progress |
| 710 | * just skip |
| 711 | */ |
| 712 | continue; |
| 713 | |
| 714 | ui = uudinfo[ctlr]; |
| 715 | uuaddr = (struct uudevice *)ui->ui_addr; |
| 716 | uutab = &uitab[ctlr]; |
| 717 | if (uuc->tu_flag++ < 40) |
| 718 | continue; |
| 719 | printf("uu%d: read stalled\n", uudata[ctlr].pk_unit); |
| 720 | #ifdef UUDEBUG |
| 721 | printf("%X %X %X %X %X %X %X\n", uuc->tu_rbptr, uuc->tu_rcnt, |
| 722 | uuc->tu_wbptr, uuc->tu_wcnt, uuc->tu_state, uuc->tu_addr, |
| 723 | uuc->tu_count); |
| 724 | #endif |
| 725 | s = splx(UUIPL); |
| 726 | uuc->tu_flag = 0; |
| 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) |
| 742 | tu_vee(&uu_pcnt[UNIT(bp->b_dev)]); |
| 743 | iodone(bp); |
| 744 | } |
| 745 | retry: |
| 746 | (void) splx(s); |
| 747 | } |
| 748 | if (active) |
| 749 | timeout(uuwatch, (caddr_t)0, hz); |
| 750 | else |
| 751 | uuwstart = 0; |
| 752 | return; |
| 753 | } |
| 754 | |
| 755 | #if !defined(VAX750) && !defined(VAX730) |
| 756 | /* |
| 757 | * Compute checksum TU58 fashion |
| 758 | */ |
| 759 | #ifdef lint |
| 760 | tuchk(word, cp, n) |
| 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 |
| 779 | tuchk(word0, wp, n) |
| 780 | register int word0; /* r11 */ |
| 781 | register char *wp; /* r10 */ |
| 782 | register int n; /* r9 */ |
| 783 | { |
| 784 | asm("loop:"); |
| 785 | asm(" addw2 (r10)+,r11"); /* add a word to sum */ |
| 786 | asm(" adwc $0,r11"); /* add in carry, end-around */ |
| 787 | asm(" acbl $2,$-2,r9,loop"); /* done yet? */ |
| 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 */ |
| 792 | asm("ok:"); |
| 793 | asm(" movl r11,r0"); /* return sum */ |
| 794 | } |
| 795 | #endif |
| 796 | |
| 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 | */ |
| 805 | tu_pee(cp) |
| 806 | char *cp; |
| 807 | { |
| 808 | register int s; |
| 809 | |
| 810 | s = splx(UUIPL); |
| 811 | if (++(*cp) > NUUQ) |
| 812 | sleep(cp, PRIBIO); |
| 813 | splx(s); |
| 814 | } |
| 815 | |
| 816 | tu_vee(cp) |
| 817 | char *cp; |
| 818 | { |
| 819 | register int s; |
| 820 | |
| 821 | s = splx(UUIPL); |
| 822 | if (--(*cp) <= NUUQ) |
| 823 | wakeup(cp); |
| 824 | splx(s); |
| 825 | } |
| 826 | #endif |
| 827 | |
| 828 | uuioctl(dev, cmd, data, flag) |
| 829 | dev_t dev; |
| 830 | caddr_t data; |
| 831 | { |
| 832 | /* |
| 833 | * add code to wind/rewind cassette here |
| 834 | */ |
| 835 | return (ENXIO); |
| 836 | } |
| 837 | |
| 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 | } |
| 845 | |
| 846 | #endif |