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