vpoio.c revision 119418
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: head/sys/dev/ppbus/vpoio.c 119418 2003-08-24 17:55:58Z obrien $"); 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#include <machine/clock.h> 41 42#endif 43 44#include "opt_vpo.h" 45 46#include <dev/ppbus/ppbio.h> 47#include <dev/ppbus/ppbconf.h> 48#include <dev/ppbus/ppb_msq.h> 49#include <dev/ppbus/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 \ 104struct 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] = (union ppb_insarg)d_pulse; \ 120 connect_epp_microseq[i].arg[2] = \ 121 connect_spp_microseq[i].arg[2] = (union ppb_insarg)c_pulse; \ 122 } \ 123} 124 125#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */) 126static 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 */) 135static 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 143static 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 148static 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 153static 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 */ 163static int 164nibble_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 \ 186struct 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 */ 203static 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 */ 219static 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 */ 233static 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 248static 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 263static 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 276static int 277vpoio_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 */ 289static int 290vpoio_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 printf("%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 */ 317static void 318vpoio_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 */ 343static int 344vpoio_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 */ 359static int 360vpoio_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 printf("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 printf("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 432error: 433 ppb_release_bus(ppbus, vpo->vpo_dev); 434 return (VP0_EINITFAILED); 435} 436 437/* 438 * vpoio_outstr() 439 */ 440static int 441vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size) 442{ 443 device_t ppbus = device_get_parent(vpo->vpo_dev); 444 int error = 0; 445 446 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 447 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 448 449 ppb_ecp_sync(ppbus); 450 451 return (error); 452} 453 454/* 455 * vpoio_instr() 456 */ 457static int 458vpoio_instr(struct vpoio_data *vpo, char *buffer, int size) 459{ 460 device_t ppbus = device_get_parent(vpo->vpo_dev); 461 int error = 0; 462 463 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 464 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 465 466 ppb_ecp_sync(ppbus); 467 468 return (error); 469} 470 471static char 472vpoio_select(struct vpoio_data *vpo, int initiator, int target) 473{ 474 device_t ppbus = device_get_parent(vpo->vpo_dev); 475 int ret; 476 477 struct ppb_microseq select_microseq[] = { 478 479 /* parameter list 480 */ 481 #define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT) 482 #define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT) 483 484 /* send the select command to the drive */ 485 MS_DASS(MS_UNKNOWN), 486 MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE), 487 MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE), 488 MS_DASS(MS_UNKNOWN), 489 MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE), 490 491 /* now, wait until the drive is ready */ 492 MS_SET(VP0_SELTMO), 493/* loop: */ MS_BRSET(H_ACK, 2 /* ready */), 494 MS_DBRA(-2 /* loop */), 495/* error: */ MS_RET(1), 496/* ready: */ MS_RET(0) 497 }; 498 499 /* initialize the select microsequence */ 500 ppb_MS_init_msq(select_microseq, 2, 501 SELECT_TARGET, 1 << target, 502 SELECT_INITIATOR, 1 << initiator); 503 504 ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 505 506 if (ret) 507 return (VP0_ESELECT_TIMEOUT); 508 509 return (0); 510} 511 512/* 513 * vpoio_wait() 514 * 515 * H_SELIN must be low. 516 * 517 * XXX should be ported to microseq 518 */ 519static char 520vpoio_wait(struct vpoio_data *vpo, int tmo) 521{ 522 DECLARE_WAIT_MICROSEQUENCE; 523 524 device_t ppbus = device_get_parent(vpo->vpo_dev); 525 int ret, err; 526 527#if 0 /* broken */ 528 if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR)) 529 return (0); 530 531 return (ppb_rstr(ppbus) & 0xf0); 532#endif 533 534 /* 535 * Return some status information. 536 * Semantics : 0xc0 = ZIP wants more data 537 * 0xd0 = ZIP wants to send more data 538 * 0xe0 = ZIP wants command 539 * 0xf0 = end of transfer, ZIP is sending status 540 */ 541 542 ppb_MS_init_msq(wait_microseq, 2, 543 WAIT_RET, (void *)&ret, 544 WAIT_TMO, tmo); 545 546 ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err); 547 548 if (err) 549 return (0); /* command timed out */ 550 551 return(ret); 552} 553 554/* 555 * vpoio_probe() 556 * 557 * Low level probe of vpo device 558 * 559 */ 560int 561vpoio_probe(device_t dev, struct vpoio_data *vpo) 562{ 563 int error; 564 565 /* ppbus dependent initialisation */ 566 vpo->vpo_dev = dev; 567 568 /* 569 * Initialize microsequence code 570 */ 571 INIT_TRIG_MICROSEQ; 572 573 /* now, try to initialise the drive */ 574 if ((error = vpoio_detect(vpo))) { 575 return (error); 576 } 577 578 return (0); 579} 580 581/* 582 * vpoio_attach() 583 * 584 * Low level attachment of vpo device 585 * 586 */ 587int 588vpoio_attach(struct vpoio_data *vpo) 589{ 590 DECLARE_NIBBLE_INBYTE_SUBMICROSEQ; 591 device_t ppbus = device_get_parent(vpo->vpo_dev); 592 int error = 0; 593 594 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 595 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 596 597 if (!vpo->vpo_nibble_inbyte_msq) 598 return (ENXIO); 599 600 bcopy((void *)nibble_inbyte_submicroseq, 601 (void *)vpo->vpo_nibble_inbyte_msq, 602 sizeof(nibble_inbyte_submicroseq)); 603 604 ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 605 INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 606 INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 607 INB_NIBBLE_F, nibble_inbyte_hook, 608 INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 609 610 /* 611 * Initialize mode dependent in/out microsequences 612 */ 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 printf("vpo%d: EPP mode\n", vpo->vpo_unit); 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 printf("vpo%d: PS2 mode\n", vpo->vpo_unit); 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 printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit); 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 return (error); 641} 642 643/* 644 * vpoio_reset_bus() 645 * 646 */ 647int 648vpoio_reset_bus(struct vpoio_data *vpo) 649{ 650 /* first, connect to the drive */ 651 if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) { 652 653#ifdef VP0_DEBUG 654 printf("%s: not in disk mode!\n", __func__); 655#endif 656 /* release ppbus */ 657 vpoio_disconnect(vpo); 658 return (1); 659 } 660 661 /* reset the SCSI bus */ 662 vpoio_reset(vpo); 663 664 /* then disconnect */ 665 vpoio_disconnect(vpo); 666 667 return (0); 668} 669 670/* 671 * vpoio_do_scsi() 672 * 673 * Send an SCSI command 674 * 675 */ 676int 677vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 678 int clen, char *buffer, int blen, int *result, int *count, 679 int *ret) 680{ 681 device_t ppbus = device_get_parent(vpo->vpo_dev); 682 register char r; 683 char l, h = 0; 684 int len, error = 0; 685 register int k; 686 687 /* 688 * enter disk state, allocate the ppbus 689 * 690 * XXX 691 * Should we allow this call to be interruptible? 692 * The only way to report the interruption is to return 693 * EIO do upper SCSI code :^( 694 */ 695 if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR))) 696 return (error); 697 698 if (!vpoio_in_disk_mode(vpo)) { 699 *ret = VP0_ECONNECT; goto error; 700 } 701 702 if ((*ret = vpoio_select(vpo,host,target))) 703 goto error; 704 705 /* 706 * Send the command ... 707 * 708 * set H_SELIN low for vpoio_wait(). 709 */ 710 ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE); 711 712 for (k = 0; k < clen; k++) { 713 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) { 714 *ret = VP0_ECMD_TIMEOUT; 715 goto error; 716 } 717 if (vpoio_outstr(vpo, &command[k], 1)) { 718 *ret = VP0_EPPDATA_TIMEOUT; 719 goto error; 720 } 721 } 722 723 /* 724 * Completion ... 725 */ 726 727 *count = 0; 728 for (;;) { 729 730 if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) { 731 *ret = VP0_ESTATUS_TIMEOUT; goto error; 732 } 733 734 /* stop when the ZIP wants to send status */ 735 if (r == (char)0xf0) 736 break; 737 738 if (*count >= blen) { 739 *ret = VP0_EDATA_OVERFLOW; 740 goto error; 741 } 742 743 /* if in EPP mode or writing bytes, try to transfer a sector 744 * otherwise, just send one byte 745 */ 746 if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0) 747 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 748 VP0_SECTOR_SIZE : 1; 749 else 750 len = 1; 751 752 /* ZIP wants to send data? */ 753 if (r == (char)0xc0) 754 error = vpoio_outstr(vpo, &buffer[*count], len); 755 else 756 error = vpoio_instr(vpo, &buffer[*count], len); 757 758 if (error) { 759 *ret = error; 760 goto error; 761 } 762 763 *count += len; 764 } 765 766 if (vpoio_instr(vpo, &l, 1)) { 767 *ret = VP0_EOTHER; goto error; 768 } 769 770 /* check if the ZIP wants to send more status */ 771 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0) 772 if (vpoio_instr(vpo, &h, 1)) { 773 *ret = VP0_EOTHER+2; goto error; 774 } 775 776 *result = ((int) h << 8) | ((int) l & 0xff); 777 778error: 779 /* return to printer state, release the ppbus */ 780 vpoio_disconnect(vpo); 781 return (0); 782} 783