immio.c revision 39136
16841Swpaul/*- 280029Sobrien * Copyright (c) 1998 Nicolas Souchu 36841Swpaul * All rights reserved. 46841Swpaul * 5201390Sed * Redistribution and use in source and binary forms, with or without 6201390Sed * 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 * $Id$ 27 * 28 */ 29 30/* 31 * Iomega ZIP+ Matchmaker Parallel Port Interface driver 32 * 33 * Thanks to David Campbell work on the Linux driver and the Iomega specs 34 * Thanks to Thiebault Moeglin for the drive 35 */ 36#ifdef KERNEL 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/malloc.h> 40#include <sys/buf.h> 41 42#include <machine/clock.h> 43 44#endif /* KERNEL */ 45 46#ifdef KERNEL 47#include <sys/kernel.h> 48#endif /*KERNEL */ 49 50#include <dev/ppbus/ppbconf.h> 51#include <dev/ppbus/ppb_msq.h> 52#include <dev/ppbus/vpoio.h> 53#include <dev/ppbus/ppb_1284.h> 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#define VP0_SECTOR_SIZE 512 60 61/* 62 * Microcode to execute very fast I/O sequences at the lowest bus level. 63 */ 64 65#define SELECT_TARGET MS_PARAM(6, 1, MS_TYP_CHA) 66 67#define DECLARE_SELECT_MICROSEQUENCE \ 68struct ppb_microseq select_microseq[] = { \ 69 MS_CASS(0xc), \ 70 /* first, check there is nothing holding onto the bus */ \ 71 MS_SET(VP0_SELTMO), \ 72/* _loop: */ \ 73 MS_BRCLEAR(0x8, 3 /* _ready */), \ 74 MS_DBRA(-1 /* _loop */), \ 75 MS_RET(2), /* bus busy */ \ 76/* _ready: */ \ 77 MS_CASS(0x4), \ 78 MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */), \ 79 MS_DELAY(1), \ 80 MS_CASS(0xc), \ 81 MS_CASS(0xd), \ 82 /* now, wait until the drive is ready */ \ 83 MS_SET(VP0_SELTMO), \ 84/* loop: */ \ 85 MS_BRSET(0x8, 4 /* ready */), \ 86 MS_DBRA(-1 /* loop */), \ 87/* error: */ \ 88 MS_CASS(0xc), \ 89 MS_RET(VP0_ESELECT_TIMEOUT), \ 90/* ready: */ \ 91 MS_CASS(0xc), \ 92 MS_RET(0) \ 93} 94 95static struct ppb_microseq transfer_epilog[] = { 96 MS_CASS(0x4), 97 MS_CASS(0xc), 98 MS_CASS(0xe), 99 MS_CASS(0x4), 100 MS_RET(0) 101}; 102 103#define CPP_S1 MS_PARAM(10, 2, MS_TYP_PTR) 104#define CPP_S2 MS_PARAM(13, 2, MS_TYP_PTR) 105#define CPP_S3 MS_PARAM(16, 2, MS_TYP_PTR) 106#define CPP_PARAM MS_PARAM(17, 1, MS_TYP_CHA) 107 108#define DECLARE_CPP_MICROSEQ \ 109struct ppb_microseq cpp_microseq[] = { \ 110 MS_CASS(0x0c), MS_DELAY(2), \ 111 MS_DASS(0xaa), MS_DELAY(10), \ 112 MS_DASS(0x55), MS_DELAY(10), \ 113 MS_DASS(0x00), MS_DELAY(10), \ 114 MS_DASS(0xff), MS_DELAY(10), \ 115 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */), \ 116 MS_DASS(0x87), MS_DELAY(10), \ 117 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */), \ 118 MS_DASS(0x78), MS_DELAY(10), \ 119 MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */), \ 120 MS_DASS(MS_UNKNOWN /* param */), \ 121 MS_DELAY(2), \ 122 MS_CASS(0x0c), MS_DELAY(10), \ 123 MS_CASS(0x0d), MS_DELAY(2), \ 124 MS_CASS(0x0c), MS_DELAY(10), \ 125 MS_DASS(0xff), MS_DELAY(10), \ 126 MS_RET(0) \ 127} 128 129#define NEGOCIATED_MODE MS_PARAM(2, 1, MS_TYP_CHA) 130 131#define DECLARE_NEGOCIATE_MICROSEQ \ 132static struct ppb_microseq negociate_microseq[] = { \ 133 MS_CASS(0x4), \ 134 MS_DELAY(5), \ 135 MS_DASS(MS_UNKNOWN /* mode */), \ 136 MS_DELAY(100), \ 137 MS_CASS(0x6), \ 138 MS_DELAY(5), \ 139 MS_BRSET(0x20, 6 /* continue */), \ 140 MS_DELAY(5), \ 141 MS_CASS(0x7), \ 142 MS_DELAY(5), \ 143 MS_CASS(0x6), \ 144 MS_RET(VP0_ENEGOCIATE), \ 145/* continue: */ \ 146 MS_DELAY(5), \ 147 MS_CASS(0x7), \ 148 MS_DELAY(5), \ 149 MS_CASS(0x6), \ 150 MS_RET(0) \ 151} 152 153static struct ppb_microseq reset_microseq[] = { 154 MS_CASS(0x04), 155 MS_DASS(0x40), 156 MS_DELAY(1), 157 MS_CASS(0x0c), 158 MS_CASS(0x0d), 159 MS_DELAY(50), 160 MS_CASS(0x0c), 161 MS_CASS(0x04), 162 MS_RET(0) 163}; 164 165/* 166 * nibble_inbyte_hook() 167 * 168 * Formats high and low nibble into a character 169 */ 170static int 171nibble_inbyte_hook (void *p, char *ptr) 172{ 173 struct vpo_nibble *s = (struct vpo_nibble *)p; 174 175 /* increment the buffer pointer */ 176 *ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 177 178 return (0); 179} 180 181/* 182 * Macro used to initialize each vpoio_data structure during 183 * low level attachment 184 * 185 * XXX should be converted to ppb_MS_init_msq() 186 */ 187#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) { \ 188 (vpo)->vpo_nibble_inbyte_msq[6].arg[2].p = \ 189 (void *)&(vpo)->vpo_nibble.h; \ 190 (vpo)->vpo_nibble_inbyte_msq[3].arg[2].p = \ 191 (void *)&(vpo)->vpo_nibble.l; \ 192 (vpo)->vpo_nibble_inbyte_msq[9].arg[0].f = \ 193 nibble_inbyte_hook; \ 194 (vpo)->vpo_nibble_inbyte_msq[9].arg[1].p = \ 195 (void *)&(vpo)->vpo_nibble; \ 196} 197 198/* 199 * This is the sub-microseqence for MS_GET in NIBBLE mode 200 * Retrieve the two nibbles and call the C function to generate the character 201 * and store it in the buffer (see nibble_inbyte_hook()) 202 */ 203static struct ppb_microseq nibble_inbyte_submicroseq[] = { 204 MS_CASS(0x4), 205 206/* loop: */ 207 MS_CASS(0x6), 208 MS_DELAY(1), 209 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */), 210 MS_CASS(0x5), 211 MS_DELAY(1), 212 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */), 213 MS_CASS(0x4), 214 MS_DELAY(1), 215 216 /* do a C call to format the received nibbles */ 217 MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), 218 MS_DBRA(-6 /* loop */), 219 MS_RET(0) 220}; 221 222/* 223 * This is the sub-microseqence for MS_GET in PS2 mode 224 */ 225static struct ppb_microseq ps2_inbyte_submicroseq[] = { 226 MS_CASS(0x4), 227 228/* loop: */ 229 MS_CASS(PCD | 0x6), 230 MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 231 MS_CASS(PCD | 0x5), 232 MS_DBRA(-3 /* loop */), 233 234 MS_RET(0) 235}; 236 237/* 238 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 239 */ 240static struct ppb_microseq spp_outbyte_submicroseq[] = { 241 MS_CASS(0x4), 242 243/* loop: */ 244 MS_RASSERT_P(1, MS_REG_DTR), 245 MS_CASS(0x5), 246 MS_DBRA(1), /* decrement counter */ 247 MS_RASSERT_P(1, MS_REG_DTR), 248 MS_CASS(0x0), 249 MS_DBRA(-5 /* loop */), 250 251 /* return from the put call */ 252 MS_CASS(0x4), 253 MS_RET(0) 254}; 255 256/* EPP 1.7 microsequences, ptr and len set at runtime */ 257static struct ppb_microseq epp17_outstr[] = { 258 MS_CASS(0x4), 259 MS_RASSERT_P(MS_ACCUM, MS_REG_EPP), 260 MS_CASS(0xc), 261 MS_RET(0), 262}; 263 264static struct ppb_microseq epp17_instr[] = { 265 MS_CASS(PCD | 0x4), 266 MS_RFETCH_P(MS_ACCUM, MS_REG_EPP, MS_FETCH_ALL), 267 MS_CASS(PCD | 0xc), 268 MS_RET(0), 269}; 270 271static int 272imm_disconnect(struct vpoio_data *vpo, int *disconnected) 273{ 274 DECLARE_CPP_MICROSEQ; 275 276 char s1, s2, s3; 277 int ret; 278 279 /* all should be ok */ 280 if (disconnected) 281 *disconnected = 0; 282 283 ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1, 284 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3, 285 CPP_PARAM, 0x30); 286 287 ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret); 288 289 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38) && 290 disconnected) 291 *disconnected = VP0_ECONNECT; 292 293 return (ppb_release_bus(&vpo->vpo_dev)); 294} 295 296/* 297 * how : PPB_WAIT or PPB_DONTWAIT 298 */ 299static int 300imm_connect(struct vpoio_data *vpo, int how, int *not_connected) 301{ 302 DECLARE_CPP_MICROSEQ; 303 304 char s1, s2, s3; 305 int error; 306 int ret; 307 308 /* all should be ok */ 309 if (not_connected) 310 *not_connected = 0; 311 312 if ((error = ppb_request_bus(&vpo->vpo_dev, how))) 313 return (error); 314 315 ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1, 316 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3); 317 318 /* select device 0 in compatible mode */ 319 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 320 ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret); 321 322 /* disconnect all devices */ 323 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30); 324 ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret); 325 326 if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) 327 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28); 328 else 329 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 330 331 ppb_MS_microseq(&vpo->vpo_dev, cpp_microseq, &ret); 332 333 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30) 334 && not_connected) 335 *not_connected = VP0_ECONNECT; 336 337 return (0); 338} 339 340/* 341 * imm_detect() 342 * 343 * Detect and initialise the VP0 adapter. 344 */ 345static int 346imm_detect(struct vpoio_data *vpo) 347{ 348 int error; 349 350 imm_disconnect(vpo, NULL); 351 imm_connect(vpo, PPB_DONTWAIT, &error); 352 353 if (error) 354 return (VP0_EINITFAILED); 355 356 /* send SCSI reset signal */ 357 ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, NULL); 358 359 imm_disconnect(vpo, &error); 360 361 /* ensure we are disconnected or daisy chained peripheral 362 * may cause serious problem to the disk */ 363 364 if (error) 365 return (VP0_EINITFAILED); 366 367 return (0); 368} 369 370/* 371 * imm_outstr() 372 */ 373static int 374imm_outstr(struct vpoio_data *vpo, char *buffer, int size) 375{ 376 int error = 0; 377 378 if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) 379 ppb_reset_epp_timeout(&vpo->vpo_dev); 380 381 ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error); 382 383 return (error); 384} 385 386/* 387 * imm_instr() 388 */ 389static int 390imm_instr(struct vpoio_data *vpo, char *buffer, int size) 391{ 392 int error = 0; 393 394 if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) 395 ppb_reset_epp_timeout(&vpo->vpo_dev); 396 397 ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error); 398 399 return (error); 400} 401 402static char 403imm_select(struct vpoio_data *vpo, int initiator, int target) 404{ 405 DECLARE_SELECT_MICROSEQUENCE; 406 int ret; 407 408 /* initialize the select microsequence */ 409 ppb_MS_init_msq(select_microseq, 1, 410 SELECT_TARGET, 1 << initiator | 1 << target); 411 412 ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret); 413 414 return (ret); 415} 416 417/* 418 * imm_wait() 419 * 420 * H_SELIN must be low. 421 * 422 * XXX should be ported to microseq 423 */ 424static char 425imm_wait(struct vpoio_data *vpo, int tmo) 426{ 427 428 register int k; 429 register char r; 430 431 ppb_wctr(&vpo->vpo_dev, 0xc); 432 433 /* XXX should be ported to microseq */ 434 k = 0; 435 while (!((r = ppb_rstr(&vpo->vpo_dev)) & 0x80) && (k++ < tmo)) 436 DELAY(1); 437 438 /* 439 * Return some status information. 440 * Semantics : 0x88 = ZIP+ wants more data 441 * 0x98 = ZIP+ wants to send more data 442 * 0xa8 = ZIP+ wants command 443 * 0xb8 = end of transfer, ZIP+ is sending status 444 */ 445 ppb_wctr(&vpo->vpo_dev, 0x4); 446 if (k < tmo) 447 return (r & 0xb8); 448 449 return (0); /* command timed out */ 450} 451 452static int 453imm_negociate(struct vpoio_data *vpo) 454{ 455 DECLARE_NEGOCIATE_MICROSEQ; 456 int negociate_mode; 457 int ret; 458 459 if (PPB_IN_NIBBLE_MODE(&vpo->vpo_dev)) 460 negociate_mode = 0; 461 else if (PPB_IN_PS2_MODE(&vpo->vpo_dev)) 462 negociate_mode = 1; 463 else 464 return (0); 465 466#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */ 467 ret = ppb_1284_negociate(&vpo->vpo_dev, negociate_mode); 468 469 if (ret) 470 return (VP0_ENEGOCIATE); 471#endif 472 473 ppb_MS_init_msq(negociate_microseq, 1, NEGOCIATED_MODE, negociate_mode); 474 475 ppb_MS_microseq(&vpo->vpo_dev, negociate_microseq, &ret); 476 477 return (ret); 478} 479 480/* 481 * imm_probe() 482 * 483 * Low level probe of vpo device 484 * 485 */ 486struct ppb_device * 487imm_probe(struct ppb_data *ppb, struct vpoio_data *vpo) 488{ 489 490 /* ppbus dependent initialisation */ 491 vpo->vpo_dev.id_unit = vpo->vpo_unit; 492 vpo->vpo_dev.name = "vpo"; 493 vpo->vpo_dev.ppb = ppb; 494 495 /* now, try to initialise the drive */ 496 if (imm_detect(vpo)) { 497 return (NULL); 498 } 499 500 return (&vpo->vpo_dev); 501} 502 503/* 504 * imm_attach() 505 * 506 * Low level attachment of vpo device 507 * 508 */ 509int 510imm_attach(struct vpoio_data *vpo) 511{ 512 int epp; 513 514 /* 515 * Report ourselves 516 */ 517 printf("imm%d: <Iomega Matchmaker Parallel to SCSI interface> on ppbus %d\n", 518 vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit); 519 520 /* 521 * Initialize microsequence code 522 */ 523 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 524 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 525 526 if (!vpo->vpo_nibble_inbyte_msq) 527 return (0); 528 529 bcopy((void *)nibble_inbyte_submicroseq, 530 (void *)vpo->vpo_nibble_inbyte_msq, 531 sizeof(nibble_inbyte_submicroseq)); 532 533 INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo); 534 535 /* 536 * Initialize mode dependent in/out microsequences 537 */ 538 ppb_request_bus(&vpo->vpo_dev, PPB_WAIT); 539 540 /* enter NIBBLE mode to configure submsq */ 541 if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) { 542 543 ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 544 ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 545 } 546 547 /* enter PS2 mode to configure submsq */ 548 if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) { 549 550 ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq); 551 ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq); 552 } 553 554 epp = ppb_get_epp_protocol(&vpo->vpo_dev); 555 556 /* enter EPP mode to configure submsq */ 557 if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 558 559 switch (epp) { 560 case EPP_1_9: 561 case EPP_1_7: 562 ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr); 563 ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr); 564 break; 565 default: 566 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 567 epp); 568 } 569 } 570 571 /* try to enter EPP or PS/2 mode, NIBBLE otherwise */ 572 if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) { 573 switch (epp) { 574 case EPP_1_9: 575 printf("imm%d: EPP 1.9 mode\n", vpo->vpo_unit); 576 break; 577 case EPP_1_7: 578 printf("imm%d: EPP 1.7 mode\n", vpo->vpo_unit); 579 break; 580 default: 581 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 582 epp); 583 } 584 } else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) 585 printf("imm%d: PS2 mode\n", vpo->vpo_unit); 586 587 else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) 588 printf("imm%d: NIBBLE mode\n", vpo->vpo_unit); 589 590 else { 591 printf("imm%d: can't enter NIBBLE, PS2 or EPP mode\n", 592 vpo->vpo_unit); 593 594 ppb_release_bus(&vpo->vpo_dev); 595 596 free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF); 597 return (0); 598 } 599 600 ppb_release_bus(&vpo->vpo_dev); 601 602 return (1); 603} 604 605/* 606 * imm_reset_bus() 607 * 608 */ 609int 610imm_reset_bus(struct vpoio_data *vpo) 611{ 612 int not_connected; 613 614 /* first, connect to the drive */ 615 imm_connect(vpo, PPB_WAIT|PPB_INTR, ¬_connected); 616 617 if (!not_connected) { 618 619 /* reset the SCSI bus */ 620 ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, NULL); 621 622 /* then disconnect */ 623 imm_disconnect(vpo, NULL); 624 } 625 626 return (0); 627} 628 629/* 630 * imm_do_scsi() 631 * 632 * Send an SCSI command 633 * 634 */ 635int 636imm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 637 int clen, char *buffer, int blen, int *result, int *count, 638 int *ret) 639{ 640 641 register char r; 642 char l, h = 0; 643 int len, error = 0, not_connected = 0; 644 register int k; 645 int negociated = 0; 646 647 /* 648 * enter disk state, allocate the ppbus 649 * 650 * XXX 651 * Should we allow this call to be interruptible? 652 * The only way to report the interruption is to return 653 * EIO do upper SCSI code :^( 654 */ 655 if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, ¬_connected))) 656 return (error); 657 658 if (not_connected) { 659 *ret = VP0_ECONNECT; goto error; 660 } 661 662 /* 663 * Select the drive ... 664 */ 665 if ((*ret = imm_select(vpo,host,target))) 666 goto error; 667 668 /* 669 * Send the command ... 670 */ 671 for (k = 0; k < clen; k+=2) { 672 if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) { 673 *ret = VP0_ECMD_TIMEOUT; 674 goto error; 675 } 676 if (imm_outstr(vpo, &command[k], 2)) { 677 *ret = VP0_EPPDATA_TIMEOUT; 678 goto error; 679 } 680 } 681 682 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 683 *ret = VP0_ESTATUS_TIMEOUT; goto error; 684 } 685 686 if ((r & 0x30) == 0x10) { 687 if (imm_negociate(vpo)) { 688 *ret = VP0_ENEGOCIATE; 689 goto error; 690 } else 691 negociated = 1; 692 } 693 694 /* 695 * Complete transfer ... 696 */ 697 *count = 0; 698 for (;;) { 699 700 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 701 *ret = VP0_ESTATUS_TIMEOUT; goto error; 702 } 703 704 /* stop when the ZIP+ wants to send status */ 705 if (r == (char)0xb8) 706 break; 707 708 if (*count >= blen) { 709 *ret = VP0_EDATA_OVERFLOW; 710 goto error; 711 } 712 713 /* ZIP+ wants to send data? */ 714 if (r == (char)0x88) { 715 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 716 VP0_SECTOR_SIZE : 2; 717 718 error = imm_outstr(vpo, &buffer[*count], len); 719 } else { 720 if (!PPB_IN_EPP_MODE(&vpo->vpo_dev)) 721 len = 1; 722 else 723 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 724 VP0_SECTOR_SIZE : 1; 725 726 error = imm_instr(vpo, &buffer[*count], len); 727 } 728 729 if (error) { 730 *ret = error; 731 goto error; 732 } 733 734 *count += len; 735 } 736 737 if ((PPB_IN_NIBBLE_MODE(&vpo->vpo_dev) || 738 PPB_IN_PS2_MODE(&vpo->vpo_dev)) && negociated) 739 ppb_MS_microseq(&vpo->vpo_dev, transfer_epilog, NULL); 740 741 /* 742 * Retrieve status ... 743 */ 744 if (imm_negociate(vpo)) { 745 *ret = VP0_ENEGOCIATE; 746 goto error; 747 } else 748 negociated = 1; 749 750 if (imm_instr(vpo, &l, 1)) { 751 *ret = VP0_EOTHER; goto error; 752 } 753 754 /* check if the ZIP+ wants to send more status */ 755 if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8) 756 if (imm_instr(vpo, &h, 1)) { 757 *ret = VP0_EOTHER+2; goto error; 758 } 759 760 *result = ((int) h << 8) | ((int) l & 0xff); 761 762error: 763 if ((PPB_IN_NIBBLE_MODE(&vpo->vpo_dev) || 764 PPB_IN_PS2_MODE(&vpo->vpo_dev)) && negociated) 765 ppb_MS_microseq(&vpo->vpo_dev, transfer_epilog, NULL); 766 767 /* return to printer state, release the ppbus */ 768 imm_disconnect(vpo, NULL); 769 770 return (0); 771} 772