vpoio.c revision 39134
1/*- 2 * Copyright (c) 1998 Nicolas Souchu 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: vpoio.c,v 1.1.2.6 1998/08/07 01:59:49 son Exp $ 27 * 28 */ 29 30#ifdef KERNEL 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/malloc.h> 34#include <sys/buf.h> 35 36#include <machine/clock.h> 37 38#endif /* KERNEL */ 39 40#ifdef KERNEL 41#include <sys/kernel.h> 42#endif /*KERNEL */ 43 44#include <dev/ppbus/ppbconf.h> 45#include <dev/ppbus/ppb_msq.h> 46#include <dev/ppbus/vpoio.h> 47 48/* 49 * The driver pools the drive. We may add a timeout queue to avoid 50 * active polling on nACK. I've tried this but it leads to unreliable 51 * transfers 52 */ 53#define VP0_SELTMO 5000 /* select timeout */ 54#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 55#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 56 57/* 58 * Actually, VP0 timings are more accurate (about few 16MHZ cycles), 59 * but succeeding in respecting such timings leads to architecture 60 * dependent considerations. 61 */ 62#define VP0_PULSE 1 63 64#define VP0_SECTOR_SIZE 512 65#define VP0_BUFFER_SIZE 0x12000 66 67#define n(flags) (~(flags) & (flags)) 68 69/* 70 * VP0 connections. 71 */ 72#define H_AUTO n(AUTOFEED) 73#define H_nAUTO AUTOFEED 74#define H_STROBE n(STROBE) 75#define H_nSTROBE STROBE 76#define H_BSY n(nBUSY) 77#define H_nBSY nBUSY 78#define H_SEL SELECT 79#define H_nSEL n(SELECT) 80#define H_ERR PERROR 81#define H_nERR n(PERROR) 82#define H_ACK nACK 83#define H_nACK n(nACK) 84#define H_FLT nFAULT 85#define H_nFLT n(nFAULT) 86#define H_SELIN n(SELECTIN) 87#define H_nSELIN SELECTIN 88#define H_INIT nINIT 89#define H_nINIT n(nINIT) 90 91/* 92 * Microcode to execute very fast I/O sequences at the lowest bus level. 93 */ 94 95/* call this macro to initialize connect/disconnect microsequences */ 96#define INIT_TRIG_MICROSEQ { \ 97 int i; \ 98 for (i=1; i <= 7; i+=2) { \ 99 disconnect_microseq[i].arg[2] = (void *)d_pulse; \ 100 connect_epp_microseq[i].arg[2] = \ 101 connect_spp_microseq[i].arg[2] = (void *)c_pulse; \ 102 } \ 103} 104 105#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */) 106static char d_pulse[] = { 107 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 108 H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE, 109 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 110 H_AUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 111 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 112}; 113 114#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */) 115static char c_pulse[] = { 116 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 117 H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 118 H_nAUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 119 H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 120 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 121}; 122 123static struct ppb_microseq disconnect_microseq[] = { 124 MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse, 125 MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0) 126}; 127 128static struct ppb_microseq connect_epp_microseq[] = { 129 MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 130 MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0) 131}; 132 133static struct ppb_microseq connect_spp_microseq[] = { 134 MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 135 MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0) 136}; 137 138/* 139 * nibble_inbyte_hook() 140 * 141 * Formats high and low nibble into a character 142 */ 143static int 144nibble_inbyte_hook (void *p, char *ptr) 145{ 146 struct vpo_nibble *s = (struct vpo_nibble *)p; 147 148 /* increment the buffer pointer */ 149 *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 150 151 return (0); 152} 153 154/* 155 * Macro used to initialize each vpoio_data structure during 156 * low level attachment 157 * 158 * XXX should be converted to ppb_MS_init_msq() 159 */ 160#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) { \ 161 (vpo)->vpo_nibble_inbyte_msq[2].arg[2].p = \ 162 (void *)&(vpo)->vpo_nibble.h; \ 163 (vpo)->vpo_nibble_inbyte_msq[4].arg[2].p = \ 164 (void *)&(vpo)->vpo_nibble.l; \ 165 (vpo)->vpo_nibble_inbyte_msq[5].arg[0].f = \ 166 nibble_inbyte_hook; \ 167 (vpo)->vpo_nibble_inbyte_msq[5].arg[1].p = \ 168 (void *)&(vpo)->vpo_nibble; \ 169} 170 171/* 172 * This is the sub-microseqence for MS_GET in NIBBLE mode 173 * Retrieve the two nibbles and call the C function to generate the character 174 * and store it in the buffer (see nibble_inbyte_hook()) 175 */ 176static struct ppb_microseq nibble_inbyte_submicroseq[] = { 177 178/* loop: */ 179 MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE), 180 MS_DELAY(VP0_PULSE), 181 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */), 182 MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE), 183 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */), 184 185 /* do a C call to format the received nibbles */ 186 MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), 187 MS_DBRA(-6 /* loop */), 188 189 MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 190 MS_RET(0) 191}; 192 193/* 194 * This is the sub-microseqence for MS_GET in PS2 mode 195 */ 196static struct ppb_microseq ps2_inbyte_submicroseq[] = { 197 MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 198 199/* loop: */ 200 MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 201 MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE), 202 MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE), 203 MS_DBRA(-3 /* loop */), 204 205 MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 206 MS_RET(0) 207}; 208 209/* 210 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 211 */ 212static struct ppb_microseq spp_outbyte_submicroseq[] = { 213 214/* loop: */ 215 MS_RASSERT_P(1, MS_REG_DTR), 216 MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 217 MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 218 MS_DELAY(VP0_PULSE), 219 MS_DBRA(-4 /* loop */), 220 221 /* return from the put call */ 222 MS_RET(0) 223}; 224 225/* EPP 1.7 microsequences, ptr and len set at runtime */ 226static struct ppb_microseq epp17_outstr_body[] = { 227 MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE), 228 229/* loop: */ 230 MS_RASSERT_P(1, MS_REG_EPP), 231 MS_BRSET(TIMEOUT, 4 /* error */), /* EPP timeout? */ 232 MS_DBRA(-2 /* loop */), 233 234 MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 235 MS_RET(0), 236/* error: */ 237 MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 238 MS_RET(1) 239}; 240 241static struct ppb_microseq epp17_instr_body[] = { 242 MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE), 243 244/* loop: */ 245 MS_RFETCH_P(1, MS_REG_EPP, MS_FETCH_ALL), 246 MS_BRSET(TIMEOUT, 4 /* error */), /* EPP timeout? */ 247 MS_DBRA(-2 /* loop */), 248 249 MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 250 MS_RET(0), 251/* error: */ 252 MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE), 253 MS_RET(1) 254}; 255 256static struct ppb_microseq in_disk_mode[] = { 257 MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 258 MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 259 260 MS_BRCLEAR(H_FLT, 4 /* error */), 261 MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 262 MS_BRSET(H_FLT, 2 /* error */), 263 264 MS_RET(0), 265/* error: */ 266 MS_RET(1) 267}; 268 269static int 270vpoio_disconnect(struct vpoio_data *vpo) 271{ 272 int ret; 273 274 ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret); 275 return (ppb_release_bus(&vpo->vpo_dev)); 276} 277 278/* 279 * how : PPB_WAIT or PPB_DONTWAIT 280 */ 281static int 282vpoio_connect(struct vpoio_data *vpo, int how) 283{ 284 int error; 285 int ret; 286 287 if ((error = ppb_request_bus(&vpo->vpo_dev, how))) 288 return error; 289 290 if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) 291 ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret); 292 else 293 ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret); 294 295 return (0); 296} 297 298/* 299 * vpoio_reset() 300 * 301 * SCSI reset signal, the drive must be in disk mode 302 */ 303static void 304vpoio_reset (struct vpoio_data *vpo) 305{ 306 int ret; 307 308 struct ppb_microseq reset_microseq[] = { 309 310 #define INITIATOR MS_PARAM(0, 1, MS_TYP_INT) 311 312 MS_DASS(MS_UNKNOWN), 313 MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 314 MS_DELAY(25), 315 MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE), 316 MS_RET(0) 317 }; 318 319 ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR); 320 ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, &ret); 321 322 return; 323} 324 325/* 326 * vpoio_in_disk_mode() 327 */ 328static int 329vpoio_in_disk_mode(struct vpoio_data *vpo) 330{ 331 int ret; 332 333 ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 334 335 return (ret); 336} 337 338/* 339 * vpoio_detect() 340 * 341 * Detect and initialise the VP0 adapter. 342 */ 343static int 344vpoio_detect(struct vpoio_data *vpo) 345{ 346 vpoio_disconnect(vpo); 347 vpoio_connect(vpo, PPB_DONTWAIT); 348 349 if (vpoio_in_disk_mode(vpo)) { 350 vpoio_disconnect(vpo); 351 return (VP0_EINITFAILED); 352 } 353 354 /* send SCSI reset signal */ 355 vpoio_reset(vpo); 356 357 vpoio_disconnect(vpo); 358 359 /* ensure we are disconnected or daisy chained peripheral 360 * may cause serious problem to the disk */ 361 362 if (!vpoio_in_disk_mode(vpo)) 363 return (VP0_EINITFAILED); 364 365 return (0); 366} 367 368/* 369 * vpoio_outstr() 370 */ 371static int 372vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) 373{ 374 375 int error = 0; 376 377 ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error); 378 379#if 0 380 /* XXX EPP 1.9 not implemented with microsequences */ 381 else { 382 383 ppb_reset_epp_timeout(&vpo->vpo_dev); 384 ppb_wctr(&vpo->vpo_dev, 385 H_AUTO | H_SELIN | H_INIT | H_STROBE); 386 387 if (((long) buffer | size) & 0x03) 388 ppb_outsb_epp(&vpo->vpo_dev, 389 buffer, size); 390 else 391 ppb_outsl_epp(&vpo->vpo_dev, 392 buffer, size/4); 393 394 if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { 395 error = VP0_EPPDATA_TIMEOUT; 396 goto error; 397 } 398 399 ppb_wctr(&vpo->vpo_dev, 400 H_AUTO | H_nSELIN | H_INIT | H_STROBE); 401 } 402 /* ppb_ecp_sync(&vpo->vpo_dev); */ 403#endif 404 405 return (error); 406} 407 408/* 409 * vpoio_instr() 410 */ 411static int 412vpoio_instr(struct vpoio_data *vpo, char *buffer, int size) 413{ 414 415 register int k; 416 int error = 0; 417 int r, mode, epp; 418 419 ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error); 420 421#if 0 422 /* XXX EPP 1.9 not implemented with microsequences */ 423 else { 424 425 ppb_reset_epp_timeout(&vpo->vpo_dev); 426 ppb_wctr(&vpo->vpo_dev, PCD | 427 H_AUTO | H_SELIN | H_INIT | H_STROBE); 428 429 if (((long) buffer | size) & 0x03) 430 ppb_insb_epp(&vpo->vpo_dev, 431 buffer, size); 432 else 433 ppb_insl_epp(&vpo->vpo_dev, 434 buffer, size/4); 435 436 if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { 437 error = VP0_EPPDATA_TIMEOUT; 438 goto error; 439 } 440 441 ppb_wctr(&vpo->vpo_dev, PCD | 442 H_AUTO | H_nSELIN | H_INIT | H_STROBE); 443 } 444 /* ppb_ecp_sync(&vpo->vpo_dev); */ 445#endif 446 447 return (error); 448} 449 450static char 451vpoio_select(struct vpoio_data *vpo, int initiator, int target) 452{ 453 register int k; 454 int ret; 455 456 struct ppb_microseq select_microseq[] = { 457 458 /* parameter list 459 */ 460 #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) 461 #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) 462 463 /* send the select command to the drive */ 464 MS_DASS(MS_UNKNOWN), 465 MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 466 MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 467 MS_DASS(MS_UNKNOWN), 468 MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 469 470 /* now, wait until the drive is ready */ 471 MS_SET(VP0_SELTMO), 472/* loop: */ MS_BRSET(H_ACK, 3 /* ready */), 473 MS_DBRA(-1 /* loop */), 474/* error: */ MS_RET(1), 475/* ready: */ MS_RET(0) 476 }; 477 478 /* initialize the select microsequence */ 479 ppb_MS_init_msq(select_microseq, 2, 480 SELECT_TARGET, 1 << target, 481 SELECT_INITIATOR, 1 << initiator); 482 483 ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret); 484 485 if (ret) 486 return (VP0_ESELECT_TIMEOUT); 487 488 return (0); 489} 490 491/* 492 * vpoio_wait() 493 * 494 * H_SELIN must be low. 495 * 496 * XXX should be ported to microseq 497 */ 498static char 499vpoio_wait(struct vpoio_data *vpo, int tmo) 500{ 501 502 register int k; 503 register char r; 504 505#if 0 /* broken */ 506 if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR)) 507 return (0); 508 509 return (ppb_rstr(&vpo->vpo_dev) & 0xf0); 510#endif 511 512 /* XXX should be ported to microseq */ 513 k = 0; 514 while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo)) 515 ; 516 517 /* 518 * Return some status information. 519 * Semantics : 0xc0 = ZIP wants more data 520 * 0xd0 = ZIP wants to send more data 521 * 0xe0 = ZIP wants command 522 * 0xf0 = end of transfer, ZIP is sending status 523 */ 524 if (k < tmo) 525 return (r & 0xf0); 526 527 return (0); /* command timed out */ 528} 529 530/* 531 * vpoio_probe() 532 * 533 * Low level probe of vpo device 534 * 535 */ 536struct ppb_device * 537vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo) 538{ 539 540 /* ppbus dependent initialisation */ 541 vpo->vpo_dev.id_unit = vpo->vpo_unit; 542 vpo->vpo_dev.name = "vpo"; 543 vpo->vpo_dev.ppb = ppb; 544 545 /* 546 * Initialize microsequence code 547 */ 548 INIT_TRIG_MICROSEQ; 549 550 /* now, try to initialise the drive */ 551 if (vpoio_detect(vpo)) { 552 return (NULL); 553 } 554 555 return (&vpo->vpo_dev); 556} 557 558/* 559 * vpoio_attach() 560 * 561 * Low level attachment of vpo device 562 * 563 */ 564int 565vpoio_attach(struct vpoio_data *vpo) 566{ 567 int epp; 568 569 /* 570 * Report ourselves 571 */ 572 printf("vpo%d: <Iomega VPI0 Parallel to SCSI interface> on ppbus %d\n", 573 vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit); 574 575 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 576 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 577 578 if (!vpo->vpo_nibble_inbyte_msq) 579 return (0); 580 581 bcopy((void *)nibble_inbyte_submicroseq, 582 (void *)vpo->vpo_nibble_inbyte_msq, 583 sizeof(nibble_inbyte_submicroseq)); 584 585 INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo); 586 587 /* 588 * Initialize mode dependent in/out microsequences 589 */ 590 ppb_request_bus(&vpo->vpo_dev, PPB_WAIT); 591 592 /* enter NIBBLE mode to configure submsq */ 593 if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) { 594 595 ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 596 597 ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 598 } 599 600 /* enter PS2 mode to configure submsq */ 601 if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) { 602 603 ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq); 604 605 ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 606 } 607 608 epp = ppb_get_epp_protocol(&vpo->vpo_dev); 609 610 /* enter EPP mode to configure submsq */ 611 if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 612 613 switch (epp) { 614 case EPP_1_9: 615 /* XXX EPP 1.9 support should be improved */ 616 case EPP_1_7: 617 ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body); 618 619 ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body); 620 break; 621 default: 622 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 623 epp); 624 } 625 } 626 627 /* try to enter EPP or PS/2 mode, NIBBLE otherwise */ 628 if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 629 switch (epp) { 630 case EPP_1_9: 631 printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit); 632 break; 633 case EPP_1_7: 634 printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit); 635 break; 636 default: 637 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 638 epp); 639 } 640 } else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) 641 printf("vpo%d: PS2 mode\n", vpo->vpo_unit); 642 643 else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) 644 printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); 645 646 else { 647 printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n", 648 vpo->vpo_unit); 649 650 ppb_release_bus(&vpo->vpo_dev); 651 652 free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF); 653 return (0); 654 } 655 656 ppb_release_bus(&vpo->vpo_dev); 657 658 return (1); 659} 660 661/* 662 * vpoio_reset_bus() 663 * 664 */ 665int 666vpoio_reset_bus(struct vpoio_data *vpo) 667{ 668 /* first, connect to the drive */ 669 if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || vpoio_in_disk_mode(vpo)) { 670 /* release ppbus */ 671 vpoio_disconnect(vpo); 672 return (1); 673 } 674 675 /* reset the SCSI bus */ 676 vpoio_reset(vpo); 677 678 /* then disconnect */ 679 vpoio_disconnect(vpo); 680 681 return (0); 682} 683 684/* 685 * vpoio_do_scsi() 686 * 687 * Send an SCSI command 688 * 689 */ 690int 691vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 692 int clen, char *buffer, int blen, int *result, int *count, 693 int *ret) 694{ 695 696 register char r; 697 char l, h = 0; 698 int len, error = 0; 699 register int k; 700 701 /* 702 * enter disk state, allocate the ppbus 703 * 704 * XXX 705 * Should we allow this call to be interruptible? 706 * The only way to report the interruption is to return 707 * EIO do upper SCSI code :^( 708 */ 709 if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) 710 return (error); 711 712 if (vpoio_in_disk_mode(vpo)) { 713 *ret = VP0_ECONNECT; goto error; 714 } 715 716 if ((*ret = vpoio_select(vpo,host,target))) 717 goto error; 718 719 /* 720 * Send the command ... 721 * 722 * set H_SELIN low for vpoio_wait(). 723 */ 724 ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 725 726 for (k = 0; k < clen; k++) { 727 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 728 *ret = VP0_ECMD_TIMEOUT; 729 goto error; 730 } 731 if (vpoio_outstr(vpo, &command[k], 1)) { 732 *ret = VP0_EPPDATA_TIMEOUT; 733 goto error; 734 } 735 } 736 737 /* 738 * Completion ... 739 */ 740 741 *count = 0; 742 for (;;) { 743 744 if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 745 *ret = VP0_ESTATUS_TIMEOUT; goto error; 746 } 747 748 /* stop when the ZIP wants to send status */ 749 if (r == (char)0xf0) 750 break; 751 752 if (*count >= blen) { 753 *ret = VP0_EDATA_OVERFLOW; 754 goto error; 755 } 756 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 757 VP0_SECTOR_SIZE : 1; 758 759 /* ZIP wants to send data? */ 760 if (r == (char)0xc0) 761 error = vpoio_outstr(vpo, &buffer[*count], len); 762 else 763 error = vpoio_instr(vpo, &buffer[*count], len); 764 765 if (error) { 766 *ret = error; 767 goto error; 768 } 769 770 *count += len; 771 } 772 773 if (vpoio_instr(vpo, &l, 1)) { 774 *ret = VP0_EOTHER; goto error; 775 } 776 777 /* check if the ZIP wants to send more status */ 778 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 779 if (vpoio_instr(vpo, &h, 1)) { 780 *ret = VP0_EOTHER+2; goto error; 781 } 782 783 *result = ((int) h << 8) | ((int) l & 0xff); 784 785error: 786 /* return to printer state, release the ppbus */ 787 vpoio_disconnect(vpo); 788 return (0); 789} 790