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.8 2006/12/22 23:26:17 swildner 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 kprintf("%s: can't request bus!\n", __func__); 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 kprintf("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 kprintf("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 = kmalloc(sizeof(nibble_inbyte_submicroseq), 597 M_DEVBUF, M_WAITOK); 598 599 bcopy((void *)nibble_inbyte_submicroseq, 600 (void *)vpo->vpo_nibble_inbyte_msq, 601 sizeof(nibble_inbyte_submicroseq)); 602 603 ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 604 INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 605 INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 606 INB_NIBBLE_F, nibble_inbyte_hook, 607 INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 608 609 /* 610 * Initialize mode dependent in/out microsequences 611 */ 612 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT))) 613 goto error; 614 615 /* ppbus sets automatically the last mode entered during detection */ 616 switch (vpo->vpo_mode_found) { 617 case VP0_MODE_EPP: 618 ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body); 619 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body); 620 kprintf("vpo%d: EPP mode\n", vpo->vpo_unit); 621 break; 622 case VP0_MODE_PS2: 623 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 624 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 625 kprintf("vpo%d: PS2 mode\n", vpo->vpo_unit); 626 break; 627 case VP0_MODE_NIBBLE: 628 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 629 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 630 kprintf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); 631 break; 632 default: 633 panic("vpo: unknown mode %d", vpo->vpo_mode_found); 634 } 635 636 ppb_release_bus(ppbus, vpo->vpo_dev); 637 638 error: 639 return (error); 640 } 641 642 /* 643 * vpoio_reset_bus() 644 * 645 */ 646 int 647 vpoio_reset_bus(struct vpoio_data *vpo) 648 { 649 /* first, connect to the drive */ 650 if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) { 651 652 #ifdef VP0_DEBUG 653 kprintf("%s: not in disk mode!\n", __func__); 654 #endif 655 /* release ppbus */ 656 vpoio_disconnect(vpo); 657 return (1); 658 } 659 660 /* reset the SCSI bus */ 661 vpoio_reset(vpo); 662 663 /* then disconnect */ 664 vpoio_disconnect(vpo); 665 666 return (0); 667 } 668 669 /* 670 * vpoio_do_scsi() 671 * 672 * Send an SCSI command 673 * 674 */ 675 int 676 vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 677 int clen, char *buffer, int blen, int *result, int *count, 678 int *ret) 679 { 680 device_t ppbus = device_get_parent(vpo->vpo_dev); 681 char r; 682 char l, h = 0; 683 int len, error = 0; 684 int k; 685 686 /* 687 * enter disk state, allocate the ppbus 688 * 689 * XXX 690 * Should we allow this call to be interruptible? 691 * The only way to report the interruption is to return 692 * EIO do upper SCSI code :^( 693 */ 694 if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) 695 return (error); 696 697 if (!vpoio_in_disk_mode(vpo)) { 698 *ret = VP0_ECONNECT; goto error; 699 } 700 701 if ((*ret = vpoio_select(vpo,host,target))) 702 goto error; 703 704 /* 705 * Send the command ... 706 * 707 * set H_SELIN low for vpoio_wait(). 708 */ 709 ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 710 711 for (k = 0; k < clen; k++) { 712 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 713 *ret = VP0_ECMD_TIMEOUT; 714 goto error; 715 } 716 if (vpoio_outstr(vpo, &command[k], 1)) { 717 *ret = VP0_EPPDATA_TIMEOUT; 718 goto error; 719 } 720 } 721 722 /* 723 * Completion ... 724 */ 725 726 *count = 0; 727 for (;;) { 728 729 if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 730 *ret = VP0_ESTATUS_TIMEOUT; goto error; 731 } 732 733 /* stop when the ZIP wants to send status */ 734 if (r == (char)0xf0) 735 break; 736 737 if (*count >= blen) { 738 *ret = VP0_EDATA_OVERFLOW; 739 goto error; 740 } 741 742 /* if in EPP mode or writing bytes, try to transfer a sector 743 * otherwise, just send one byte 744 */ 745 if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0) 746 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 747 VP0_SECTOR_SIZE : 1; 748 else 749 len = 1; 750 751 /* ZIP wants to send data? */ 752 if (r == (char)0xc0) 753 error = vpoio_outstr(vpo, &buffer[*count], len); 754 else 755 error = vpoio_instr(vpo, &buffer[*count], len); 756 757 if (error) { 758 *ret = error; 759 goto error; 760 } 761 762 *count += len; 763 } 764 765 if (vpoio_instr(vpo, &l, 1)) { 766 *ret = VP0_EOTHER; goto error; 767 } 768 769 /* check if the ZIP wants to send more status */ 770 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 771 if (vpoio_instr(vpo, &h, 1)) { 772 *ret = VP0_EOTHER+2; goto error; 773 } 774 775 *result = ((int) h << 8) | ((int) l & 0xff); 776 777 error: 778 /* return to printer state, release the ppbus */ 779 vpoio_disconnect(vpo); 780 return (0); 781 } 782