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