vpoio.c revision 103736
1/*- 2 * Copyright (c) 1998, 1999 Nicolas Souchu 3 * Copyright (c) 2000 Alcove - Nicolas Souchu 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/dev/ppbus/vpoio.c 103736 2002-09-21 08:44:51Z phk $ 28 * 29 */ 30 31#ifdef _KERNEL 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/module.h> 35#include <sys/bus.h> 36#include <sys/malloc.h> 37 38#include <machine/clock.h> 39 40#endif 41 42#include "opt_vpo.h" 43 44#include <dev/ppbus/ppbio.h> 45#include <dev/ppbus/ppbconf.h> 46#include <dev/ppbus/ppb_msq.h> 47#include <dev/ppbus/vpoio.h> 48 49#include "ppbus_if.h" 50 51/* 52 * The driver pools the drive. We may add a timeout queue to avoid 53 * active polling on nACK. I've tried this but it leads to unreliable 54 * transfers 55 */ 56#define VP0_SELTMO 5000 /* select timeout */ 57#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 58#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 59 60/* 61 * Actually, VP0 timings are more accurate (about few 16MHZ cycles), 62 * but succeeding in respecting such timings leads to architecture 63 * dependent considerations. 64 */ 65#define VP0_PULSE 1 66 67#define VP0_SECTOR_SIZE 512 68#define VP0_BUFFER_SIZE 0x12000 69 70#define n(flags) (~(flags) & (flags)) 71 72/* 73 * VP0 connections. 74 */ 75#define H_AUTO n(AUTOFEED) 76#define H_nAUTO AUTOFEED 77#define H_STROBE n(STROBE) 78#define H_nSTROBE STROBE 79#define H_BSY n(nBUSY) 80#define H_nBSY nBUSY 81#define H_SEL SELECT 82#define H_nSEL n(SELECT) 83#define H_ERR PERROR 84#define H_nERR n(PERROR) 85#define H_ACK nACK 86#define H_nACK n(nACK) 87#define H_FLT nFAULT 88#define H_nFLT n(nFAULT) 89#define H_SELIN n(SELECTIN) 90#define H_nSELIN SELECTIN 91#define H_INIT nINIT 92#define H_nINIT n(nINIT) 93 94/* 95 * Microcode to execute very fast I/O sequences at the lowest bus level. 96 */ 97 98#define WAIT_RET MS_PARAM(4, 2, MS_TYP_PTR) 99#define WAIT_TMO MS_PARAM(0, 0, MS_TYP_INT) 100 101#define DECLARE_WAIT_MICROSEQUENCE \ 102struct ppb_microseq wait_microseq[] = { \ 103 MS_SET(MS_UNKNOWN), \ 104 /* loop */ \ 105 MS_BRSET(nBUSY, 2 /* ready */), \ 106 MS_DBRA(-2 /* loop */), \ 107 MS_RET(1), /* timed out */ \ 108 /* ready */ \ 109 MS_RFETCH(MS_REG_STR, 0xf0, MS_UNKNOWN), \ 110 MS_RET(0) /* no error */ \ 111} 112 113/* call this macro to initialize connect/disconnect microsequences */ 114#define INIT_TRIG_MICROSEQ { \ 115 int i; \ 116 for (i=1; i <= 7; i+=2) { \ 117 disconnect_microseq[i].arg[2] = (union ppb_insarg)d_pulse; \ 118 connect_epp_microseq[i].arg[2] = \ 119 connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \ 120 } \ 121} 122 123#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */) 124static char d_pulse[] = { 125 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 126 H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE, 127 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 128 H_AUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 129 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 130}; 131 132#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */) 133static char c_pulse[] = { 134 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0, 135 H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 136 H_nAUTO | H_SELIN | H_INIT | H_STROBE, VP0_PULSE, 137 H_AUTO | H_SELIN | H_INIT | H_STROBE, 0, 138 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE 139}; 140 141static struct ppb_microseq disconnect_microseq[] = { 142 MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse, 143 MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0) 144}; 145 146static struct ppb_microseq connect_epp_microseq[] = { 147 MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 148 MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0) 149}; 150 151static struct ppb_microseq connect_spp_microseq[] = { 152 MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse, 153 MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0) 154}; 155 156/* 157 * nibble_inbyte_hook() 158 * 159 * Formats high and low nibble into a character 160 */ 161static int 162nibble_inbyte_hook (void *p, char *ptr) 163{ 164 struct vpo_nibble *s = (struct vpo_nibble *)p; 165 166 /* increment the buffer pointer */ 167 *ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 168 169 return (0); 170} 171 172#define INB_NIBBLE_H MS_PARAM(2, 2, MS_TYP_PTR) 173#define INB_NIBBLE_L MS_PARAM(4, 2, MS_TYP_PTR) 174#define INB_NIBBLE_F MS_PARAM(5, 0, MS_TYP_FUN) 175#define INB_NIBBLE_P MS_PARAM(5, 1, MS_TYP_PTR) 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 */ 182 183#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \ 184struct ppb_microseq nibble_inbyte_submicroseq[] = { \ 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 /* 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 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", __func__); 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 /* Force disconnection */ 368 ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 369 370 /* Try to enter EPP mode, then connect to the drive in EPP mode */ 371 if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 372 /* call manually the microseq instead of using the appropriate function 373 * since we already requested the ppbus */ 374 ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret); 375 } 376 377 /* If EPP mode switch failed or ZIP connection in EPP mode failed, 378 * try to connect in NIBBLE mode */ 379 if (!vpoio_in_disk_mode(vpo)) { 380 381 /* The interface must be at least PS/2 or NIBBLE capable. 382 * There is no way to know if the ZIP will work with 383 * PS/2 mode since PS/2 and SPP both use the same connect 384 * sequence. One must supress PS/2 with boot flags if 385 * PS/2 mode fails (see ppc(4)). 386 */ 387 if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 388 vpo->vpo_mode_found = VP0_MODE_PS2; 389 } else { 390 if (ppb_set_mode(ppbus, PPB_NIBBLE) == -1) 391 goto error; 392 393 vpo->vpo_mode_found = VP0_MODE_NIBBLE; 394 } 395 396 /* Can't know if the interface is capable of PS/2 yet */ 397 ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret); 398 if (!vpoio_in_disk_mode(vpo)) { 399 vpo->vpo_mode_found = VP0_MODE_UNDEFINED; 400 if (bootverbose) 401 printf("vpo%d: can't connect to the drive\n", 402 vpo->vpo_unit); 403 404 /* disconnect and release the bus */ 405 ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, 406 &ret); 407 goto error; 408 } 409 } else { 410 vpo->vpo_mode_found = VP0_MODE_EPP; 411 } 412 413 /* send SCSI reset signal */ 414 vpoio_reset(vpo); 415 416 ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret); 417 418 /* ensure we are disconnected or daisy chained peripheral 419 * may cause serious problem to the disk */ 420 if (vpoio_in_disk_mode(vpo)) { 421 if (bootverbose) 422 printf("vpo%d: can't disconnect from the drive\n", 423 vpo->vpo_unit); 424 goto error; 425 } 426 427 ppb_release_bus(ppbus, vpo->vpo_dev); 428 return (0); 429 430error: 431 ppb_release_bus(ppbus, vpo->vpo_dev); 432 return (VP0_EINITFAILED); 433} 434 435/* 436 * vpoio_outstr() 437 */ 438static int 439vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) 440{ 441 device_t ppbus = device_get_parent(vpo->vpo_dev); 442 int error = 0; 443 444 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 445 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 446 447 ppb_ecp_sync(ppbus); 448 449 return (error); 450} 451 452/* 453 * vpoio_instr() 454 */ 455static int 456vpoio_instr(struct vpoio_data *vpo, char *buffer, int size) 457{ 458 device_t ppbus = device_get_parent(vpo->vpo_dev); 459 int error = 0; 460 461 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 462 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 463 464 ppb_ecp_sync(ppbus); 465 466 return (error); 467} 468 469static char 470vpoio_select(struct vpoio_data *vpo, int initiator, int target) 471{ 472 device_t ppbus = device_get_parent(vpo->vpo_dev); 473 int ret; 474 475 struct ppb_microseq select_microseq[] = { 476 477 /* parameter list 478 */ 479 #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) 480 #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) 481 482 /* send the select command to the drive */ 483 MS_DASS(MS_UNKNOWN), 484 MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 485 MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 486 MS_DASS(MS_UNKNOWN), 487 MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 488 489 /* now, wait until the drive is ready */ 490 MS_SET(VP0_SELTMO), 491/* loop: */ MS_BRSET(H_ACK, 2 /* ready */), 492 MS_DBRA(-2 /* loop */), 493/* error: */ MS_RET(1), 494/* ready: */ MS_RET(0) 495 }; 496 497 /* initialize the select microsequence */ 498 ppb_MS_init_msq(select_microseq, 2, 499 SELECT_TARGET, 1 << target, 500 SELECT_INITIATOR, 1 << initiator); 501 502 ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 503 504 if (ret) 505 return (VP0_ESELECT_TIMEOUT); 506 507 return (0); 508} 509 510/* 511 * vpoio_wait() 512 * 513 * H_SELIN must be low. 514 * 515 * XXX should be ported to microseq 516 */ 517static char 518vpoio_wait(struct vpoio_data *vpo, int tmo) 519{ 520 DECLARE_WAIT_MICROSEQUENCE; 521 522 device_t ppbus = device_get_parent(vpo->vpo_dev); 523 int ret, err; 524 525#if 0 /* broken */ 526 if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR)) 527 return (0); 528 529 return (ppb_rstr(ppbus) & 0xf0); 530#endif 531 532 /* 533 * Return some status information. 534 * Semantics : 0xc0 = ZIP wants more data 535 * 0xd0 = ZIP wants to send more data 536 * 0xe0 = ZIP wants command 537 * 0xf0 = end of transfer, ZIP is sending status 538 */ 539 540 ppb_MS_init_msq(wait_microseq, 2, 541 WAIT_RET, (void *)&ret, 542 WAIT_TMO, tmo); 543 544 ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err); 545 546 if (err) 547 return (0); /* command timed out */ 548 549 return(ret); 550} 551 552/* 553 * vpoio_probe() 554 * 555 * Low level probe of vpo device 556 * 557 */ 558int 559vpoio_probe(device_t dev, struct vpoio_data *vpo) 560{ 561 int error; 562 563 /* ppbus dependent initialisation */ 564 vpo->vpo_dev = dev; 565 566 /* 567 * Initialize microsequence code 568 */ 569 INIT_TRIG_MICROSEQ; 570 571 /* now, try to initialise the drive */ 572 if ((error = vpoio_detect(vpo))) { 573 return (error); 574 } 575 576 return (0); 577} 578 579/* 580 * vpoio_attach() 581 * 582 * Low level attachment of vpo device 583 * 584 */ 585int 586vpoio_attach(struct vpoio_data *vpo) 587{ 588 DECLARE_NIBBLE_INBYTE_SUBMICROSEQ; 589 device_t ppbus = device_get_parent(vpo->vpo_dev); 590 int error = 0; 591 592 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 593 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 594 595 if (!vpo->vpo_nibble_inbyte_msq) 596 return (ENXIO); 597 598 bcopy((void *)nibble_inbyte_submicroseq, 599 (void *)vpo->vpo_nibble_inbyte_msq, 600 sizeof(nibble_inbyte_submicroseq)); 601 602 ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 603 INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 604 INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 605 INB_NIBBLE_F, nibble_inbyte_hook, 606 INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 607 608 /* 609 * Initialize mode dependent in/out microsequences 610 */ 611 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT))) 612 goto error; 613 614 /* ppbus sets automatically the last mode entered during detection */ 615 switch (vpo->vpo_mode_found) { 616 case VP0_MODE_EPP: 617 ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body); 618 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body); 619 printf("vpo%d: EPP mode\n", vpo->vpo_unit); 620 break; 621 case VP0_MODE_PS2: 622 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 623 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 624 printf("vpo%d: PS2 mode\n", vpo->vpo_unit); 625 break; 626 case VP0_MODE_NIBBLE: 627 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 628 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 629 printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); 630 break; 631 default: 632 panic("vpo: unknown mode %d", vpo->vpo_mode_found); 633 } 634 635 ppb_release_bus(ppbus, vpo->vpo_dev); 636 637error: 638 return (error); 639} 640 641/* 642 * vpoio_reset_bus() 643 * 644 */ 645int 646vpoio_reset_bus(struct vpoio_data *vpo) 647{ 648 /* first, connect to the drive */ 649 if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) { 650 651#ifdef VP0_DEBUG 652 printf("%s: not in disk mode!\n", __func__); 653#endif 654 /* release ppbus */ 655 vpoio_disconnect(vpo); 656 return (1); 657 } 658 659 /* reset the SCSI bus */ 660 vpoio_reset(vpo); 661 662 /* then disconnect */ 663 vpoio_disconnect(vpo); 664 665 return (0); 666} 667 668/* 669 * vpoio_do_scsi() 670 * 671 * Send an SCSI command 672 * 673 */ 674int 675vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 676 int clen, char *buffer, int blen, int *result, int *count, 677 int *ret) 678{ 679 device_t ppbus = device_get_parent(vpo->vpo_dev); 680 register char r; 681 char l, h = 0; 682 int len, error = 0; 683 register int k; 684 685 /* 686 * enter disk state, allocate the ppbus 687 * 688 * XXX 689 * Should we allow this call to be interruptible? 690 * The only way to report the interruption is to return 691 * EIO do upper SCSI code :^( 692 */ 693 if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) 694 return (error); 695 696 if (!vpoio_in_disk_mode(vpo)) { 697 *ret = VP0_ECONNECT; goto error; 698 } 699 700 if ((*ret = vpoio_select(vpo,host,target))) 701 goto error; 702 703 /* 704 * Send the command ... 705 * 706 * set H_SELIN low for vpoio_wait(). 707 */ 708 ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 709 710 for (k = 0; k < clen; k++) { 711 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 712 *ret = VP0_ECMD_TIMEOUT; 713 goto error; 714 } 715 if (vpoio_outstr(vpo, &command[k], 1)) { 716 *ret = VP0_EPPDATA_TIMEOUT; 717 goto error; 718 } 719 } 720 721 /* 722 * Completion ... 723 */ 724 725 *count = 0; 726 for (;;) { 727 728 if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 729 *ret = VP0_ESTATUS_TIMEOUT; goto error; 730 } 731 732 /* stop when the ZIP wants to send status */ 733 if (r == (char)0xf0) 734 break; 735 736 if (*count >= blen) { 737 *ret = VP0_EDATA_OVERFLOW; 738 goto error; 739 } 740 741 /* if in EPP mode or writing bytes, try to transfer a sector 742 * otherwise, just send one byte 743 */ 744 if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0) 745 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 746 VP0_SECTOR_SIZE : 1; 747 else 748 len = 1; 749 750 /* ZIP wants to send data? */ 751 if (r == (char)0xc0) 752 error = vpoio_outstr(vpo, &buffer[*count], len); 753 else 754 error = vpoio_instr(vpo, &buffer[*count], len); 755 756 if (error) { 757 *ret = error; 758 goto error; 759 } 760 761 *count += len; 762 } 763 764 if (vpoio_instr(vpo, &l, 1)) { 765 *ret = VP0_EOTHER; goto error; 766 } 767 768 /* check if the ZIP wants to send more status */ 769 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 770 if (vpoio_instr(vpo, &h, 1)) { 771 *ret = VP0_EOTHER+2; goto error; 772 } 773 774 *result = ((int) h << 8) | ((int) l & 0xff); 775 776error: 777 /* return to printer state, release the ppbus */ 778 vpoio_disconnect(vpo); 779 return (0); 780} 781