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