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