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