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