vpoio.c revision 39520
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.2 1998/09/13 18:26:26 nsouch 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(1), 265/* error: */ 266 MS_RET(0) 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 int error, ret; 347 348 /* allocate the bus, then apply microsequences */ 349 if ((error = ppb_request_bus(&vpo->vpo_dev, PPB_DONTWAIT))) 350 return (error); 351 352 ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret); 353 354 if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) 355 ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret); 356 else 357 ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret); 358 359 ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 360 if (!ret) { 361 362 /* try spp mode (maybe twice or because previous mode was PS2) 363 * NIBBLE mode will be restored on next transfers if detection 364 * succeed 365 */ 366 ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE); 367 ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret); 368 369 ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 370 if (!ret) { 371 if (bootverbose) 372 printf("vpo%d: can't connect to the drive\n", 373 vpo->vpo_unit); 374 375 /* disconnect and release the bus */ 376 ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, 377 &ret); 378 goto error; 379 } 380 } 381 382 /* send SCSI reset signal */ 383 vpoio_reset(vpo); 384 385 ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret); 386 387 /* ensure we are disconnected or daisy chained peripheral 388 * may cause serious problem to the disk */ 389 390 ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret); 391 if (ret) { 392 if (bootverbose) 393 printf("vpo%d: can't disconnect from the drive\n", 394 vpo->vpo_unit); 395 goto error; 396 } 397 398 ppb_release_bus(&vpo->vpo_dev); 399 return (0); 400 401error: 402 ppb_release_bus(&vpo->vpo_dev); 403 return (VP0_EINITFAILED); 404} 405 406/* 407 * vpoio_outstr() 408 */ 409static int 410vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) 411{ 412 413 int error = 0; 414 415 ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error); 416 417#if 0 418 /* XXX EPP 1.9 not implemented with microsequences */ 419 else { 420 421 ppb_reset_epp_timeout(&vpo->vpo_dev); 422 ppb_wctr(&vpo->vpo_dev, 423 H_AUTO | H_SELIN | H_INIT | H_STROBE); 424 425 if (((long) buffer | size) & 0x03) 426 ppb_outsb_epp(&vpo->vpo_dev, 427 buffer, size); 428 else 429 ppb_outsl_epp(&vpo->vpo_dev, 430 buffer, size/4); 431 432 if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { 433 error = VP0_EPPDATA_TIMEOUT; 434 goto error; 435 } 436 437 ppb_wctr(&vpo->vpo_dev, 438 H_AUTO | H_nSELIN | H_INIT | H_STROBE); 439 } 440 /* ppb_ecp_sync(&vpo->vpo_dev); */ 441#endif 442 443 return (error); 444} 445 446/* 447 * vpoio_instr() 448 */ 449static int 450vpoio_instr(struct vpoio_data *vpo, char *buffer, int size) 451{ 452 453 register int k; 454 int error = 0; 455 int r, mode, epp; 456 457 ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error); 458 459#if 0 460 /* XXX EPP 1.9 not implemented with microsequences */ 461 else { 462 463 ppb_reset_epp_timeout(&vpo->vpo_dev); 464 ppb_wctr(&vpo->vpo_dev, PCD | 465 H_AUTO | H_SELIN | H_INIT | H_STROBE); 466 467 if (((long) buffer | size) & 0x03) 468 ppb_insb_epp(&vpo->vpo_dev, 469 buffer, size); 470 else 471 ppb_insl_epp(&vpo->vpo_dev, 472 buffer, size/4); 473 474 if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) { 475 error = VP0_EPPDATA_TIMEOUT; 476 goto error; 477 } 478 479 ppb_wctr(&vpo->vpo_dev, PCD | 480 H_AUTO | H_nSELIN | H_INIT | H_STROBE); 481 } 482 /* ppb_ecp_sync(&vpo->vpo_dev); */ 483#endif 484 485 return (error); 486} 487 488static char 489vpoio_select(struct vpoio_data *vpo, int initiator, int target) 490{ 491 register int k; 492 int ret; 493 494 struct ppb_microseq select_microseq[] = { 495 496 /* parameter list 497 */ 498 #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) 499 #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) 500 501 /* send the select command to the drive */ 502 MS_DASS(MS_UNKNOWN), 503 MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 504 MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 505 MS_DASS(MS_UNKNOWN), 506 MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 507 508 /* now, wait until the drive is ready */ 509 MS_SET(VP0_SELTMO), 510/* loop: */ MS_BRSET(H_ACK, 3 /* ready */), 511 MS_DBRA(-1 /* loop */), 512/* error: */ MS_RET(1), 513/* ready: */ MS_RET(0) 514 }; 515 516 /* initialize the select microsequence */ 517 ppb_MS_init_msq(select_microseq, 2, 518 SELECT_TARGET, 1 << target, 519 SELECT_INITIATOR, 1 << initiator); 520 521 ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret); 522 523 if (ret) 524 return (VP0_ESELECT_TIMEOUT); 525 526 return (0); 527} 528 529/* 530 * vpoio_wait() 531 * 532 * H_SELIN must be low. 533 * 534 * XXX should be ported to microseq 535 */ 536static char 537vpoio_wait(struct vpoio_data *vpo, int tmo) 538{ 539 540 register int k; 541 register char r; 542 543#if 0 /* broken */ 544 if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR)) 545 return (0); 546 547 return (ppb_rstr(&vpo->vpo_dev) & 0xf0); 548#endif 549 550 /* XXX should be ported to microseq */ 551 k = 0; 552 while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo)) 553 ; 554 555 /* 556 * Return some status information. 557 * Semantics : 0xc0 = ZIP wants more data 558 * 0xd0 = ZIP wants to send more data 559 * 0xe0 = ZIP wants command 560 * 0xf0 = end of transfer, ZIP is sending status 561 */ 562 if (k < tmo) 563 return (r & 0xf0); 564 565 return (0); /* command timed out */ 566} 567 568/* 569 * vpoio_probe() 570 * 571 * Low level probe of vpo device 572 * 573 */ 574struct ppb_device * 575vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo) 576{ 577 578 /* ppbus dependent initialisation */ 579 vpo->vpo_dev.id_unit = vpo->vpo_unit; 580 vpo->vpo_dev.name = "vpo"; 581 vpo->vpo_dev.ppb = ppb; 582 583 /* 584 * Initialize microsequence code 585 */ 586 INIT_TRIG_MICROSEQ; 587 588 /* now, try to initialise the drive */ 589 if (vpoio_detect(vpo)) { 590 return (NULL); 591 } 592 593 return (&vpo->vpo_dev); 594} 595 596/* 597 * vpoio_attach() 598 * 599 * Low level attachment of vpo device 600 * 601 */ 602int 603vpoio_attach(struct vpoio_data *vpo) 604{ 605 int epp; 606 607 /* 608 * Report ourselves 609 */ 610 printf("vpo%d: <Iomega VPI0 Parallel to SCSI interface> on ppbus %d\n", 611 vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit); 612 613 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 614 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 615 616 if (!vpo->vpo_nibble_inbyte_msq) 617 return (0); 618 619 bcopy((void *)nibble_inbyte_submicroseq, 620 (void *)vpo->vpo_nibble_inbyte_msq, 621 sizeof(nibble_inbyte_submicroseq)); 622 623 INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo); 624 625 /* 626 * Initialize mode dependent in/out microsequences 627 */ 628 ppb_request_bus(&vpo->vpo_dev, PPB_WAIT); 629 630 /* enter NIBBLE mode to configure submsq */ 631 if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) { 632 633 ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 634 635 ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 636 } 637 638 /* enter PS2 mode to configure submsq */ 639 if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) { 640 641 ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq); 642 643 ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 644 } 645 646 epp = ppb_get_epp_protocol(&vpo->vpo_dev); 647 648 /* enter EPP mode to configure submsq */ 649 if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 650 651 switch (epp) { 652 case EPP_1_9: 653 /* XXX EPP 1.9 support should be improved */ 654 case EPP_1_7: 655 ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body); 656 657 ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body); 658 break; 659 default: 660 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 661 epp); 662 } 663 } 664 665 /* try to enter EPP or PS/2 mode, NIBBLE otherwise */ 666 if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 667 switch (epp) { 668 case EPP_1_9: 669 printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit); 670 break; 671 case EPP_1_7: 672 printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit); 673 break; 674 default: 675 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 676 epp); 677 } 678 } else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) 679 printf("vpo%d: PS2 mode\n", vpo->vpo_unit); 680 681 else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) 682 printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); 683 684 else { 685 printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n", 686 vpo->vpo_unit); 687 688 ppb_release_bus(&vpo->vpo_dev); 689 690 free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF); 691 return (0); 692 } 693 694 ppb_release_bus(&vpo->vpo_dev); 695 696 return (1); 697} 698 699/* 700 * vpoio_reset_bus() 701 * 702 */ 703int 704vpoio_reset_bus(struct vpoio_data *vpo) 705{ 706 /* first, connect to the drive */ 707 if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) { 708 /* release ppbus */ 709 vpoio_disconnect(vpo); 710 return (1); 711 } 712 713 /* reset the SCSI bus */ 714 vpoio_reset(vpo); 715 716 /* then disconnect */ 717 vpoio_disconnect(vpo); 718 719 return (0); 720} 721 722/* 723 * vpoio_do_scsi() 724 * 725 * Send an SCSI command 726 * 727 */ 728int 729vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 730 int clen, char *buffer, int blen, int *result, int *count, 731 int *ret) 732{ 733 734 register char r; 735 char l, h = 0; 736 int len, error = 0; 737 register int k; 738 739 /* 740 * enter disk state, allocate the ppbus 741 * 742 * XXX 743 * Should we allow this call to be interruptible? 744 * The only way to report the interruption is to return 745 * EIO do upper SCSI code :^( 746 */ 747 if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) 748 return (error); 749 750 if (!vpoio_in_disk_mode(vpo)) { 751 *ret = VP0_ECONNECT; goto error; 752 } 753 754 if ((*ret = vpoio_select(vpo,host,target))) 755 goto error; 756 757 /* 758 * Send the command ... 759 * 760 * set H_SELIN low for vpoio_wait(). 761 */ 762 ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 763 764 for (k = 0; k < clen; k++) { 765 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 766 *ret = VP0_ECMD_TIMEOUT; 767 goto error; 768 } 769 if (vpoio_outstr(vpo, &command[k], 1)) { 770 *ret = VP0_EPPDATA_TIMEOUT; 771 goto error; 772 } 773 } 774 775 /* 776 * Completion ... 777 */ 778 779 *count = 0; 780 for (;;) { 781 782 if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 783 *ret = VP0_ESTATUS_TIMEOUT; goto error; 784 } 785 786 /* stop when the ZIP wants to send status */ 787 if (r == (char)0xf0) 788 break; 789 790 if (*count >= blen) { 791 *ret = VP0_EDATA_OVERFLOW; 792 goto error; 793 } 794 795 /* if in EPP mode or writing bytes, try to transfer a sector 796 * otherwise, just send one byte 797 */ 798 if (PPB_IN_EPP_MODE(&vpo->vpo_dev) || r == (char)0xc0) 799 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 800 VP0_SECTOR_SIZE : 1; 801 else 802 len = 1; 803 804 /* ZIP wants to send data? */ 805 if (r == (char)0xc0) 806 error = vpoio_outstr(vpo, &buffer[*count], len); 807 else 808 error = vpoio_instr(vpo, &buffer[*count], len); 809 810 if (error) { 811 *ret = error; 812 goto error; 813 } 814 815 *count += len; 816 } 817 818 if (vpoio_instr(vpo, &l, 1)) { 819 *ret = VP0_EOTHER; goto error; 820 } 821 822 /* check if the ZIP wants to send more status */ 823 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 824 if (vpoio_instr(vpo, &h, 1)) { 825 *ret = VP0_EOTHER+2; goto error; 826 } 827 828 *result = ((int) h << 8) | ((int) l & 0xff); 829 830error: 831 /* return to printer state, release the ppbus */ 832 vpoio_disconnect(vpo); 833 return (0); 834} 835