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