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