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