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