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