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