immio.c revision 78645
1/*- 2 * Copyright (c) 1998, 1999 Nicolas Souchu 3 * Copyright (c) 2001 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: head/sys/dev/ppbus/immio.c 78645 2001-06-23 06:51:52Z nsouch $ 28 * 29 */ 30 31/* 32 * Iomega ZIP+ Matchmaker Parallel Port Interface driver 33 * 34 * Thanks to David Campbell work on the Linux driver and the Iomega specs 35 * Thanks to Thiebault Moeglin for the drive 36 */ 37#ifdef _KERNEL 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/module.h> 41#include <sys/bus.h> 42#include <sys/malloc.h> 43 44#endif /* _KERNEL */ 45 46#include "opt_vpo.h" 47 48#include <dev/ppbus/ppbio.h> 49#include <dev/ppbus/ppbconf.h> 50#include <dev/ppbus/ppb_msq.h> 51#include <dev/ppbus/vpoio.h> 52#include <dev/ppbus/ppb_1284.h> 53 54#include "ppbus_if.h" 55 56#define VP0_SELTMO 5000 /* select timeout */ 57#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 58#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 59 60#define VP0_SECTOR_SIZE 512 61 62/* 63 * Microcode to execute very fast I/O sequences at the lowest bus level. 64 */ 65 66#define WAIT_RET MS_PARAM(7, 2, MS_TYP_PTR) 67#define WAIT_TMO MS_PARAM(1, 0, MS_TYP_INT) 68 69#define DECLARE_WAIT_MICROSEQUENCE \ 70struct ppb_microseq wait_microseq[] = { \ 71 MS_CASS(0x0c), \ 72 MS_SET(MS_UNKNOWN), \ 73 /* loop */ \ 74 MS_BRSET(nBUSY, 4 /* ready */), \ 75 MS_DBRA(-2 /* loop */), \ 76 MS_CASS(0x04), \ 77 MS_RET(1), /* timed out */ \ 78 /* ready */ \ 79 MS_CASS(0x04), \ 80 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN ), \ 81 MS_RET(0) /* no error */ \ 82} 83 84#define SELECT_TARGET MS_PARAM(6, 1, MS_TYP_CHA) 85 86#define DECLARE_SELECT_MICROSEQUENCE \ 87struct ppb_microseq select_microseq[] = { \ 88 MS_CASS(0xc), \ 89 /* first, check there is nothing holding onto the bus */ \ 90 MS_SET(VP0_SELTMO), \ 91/* _loop: */ \ 92 MS_BRCLEAR(0x8, 2 /* _ready */), \ 93 MS_DBRA(-2 /* _loop */), \ 94 MS_RET(2), /* bus busy */ \ 95/* _ready: */ \ 96 MS_CASS(0x4), \ 97 MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */), \ 98 MS_DELAY(1), \ 99 MS_CASS(0xc), \ 100 MS_CASS(0xd), \ 101 /* now, wait until the drive is ready */ \ 102 MS_SET(VP0_SELTMO), \ 103/* loop: */ \ 104 MS_BRSET(0x8, 3 /* ready */), \ 105 MS_DBRA(-2 /* loop */), \ 106/* error: */ \ 107 MS_CASS(0xc), \ 108 MS_RET(VP0_ESELECT_TIMEOUT), \ 109/* ready: */ \ 110 MS_CASS(0xc), \ 111 MS_RET(0) \ 112} 113 114static struct ppb_microseq transfer_epilog[] = { 115 MS_CASS(0x4), 116 MS_CASS(0xc), 117 MS_CASS(0xe), 118 MS_CASS(0x4), 119 MS_RET(0) 120}; 121 122#define CPP_S1 MS_PARAM(10, 2, MS_TYP_PTR) 123#define CPP_S2 MS_PARAM(13, 2, MS_TYP_PTR) 124#define CPP_S3 MS_PARAM(16, 2, MS_TYP_PTR) 125#define CPP_PARAM MS_PARAM(17, 1, MS_TYP_CHA) 126 127#define DECLARE_CPP_MICROSEQ \ 128struct ppb_microseq cpp_microseq[] = { \ 129 MS_CASS(0x0c), MS_DELAY(2), \ 130 MS_DASS(0xaa), MS_DELAY(10), \ 131 MS_DASS(0x55), MS_DELAY(10), \ 132 MS_DASS(0x00), MS_DELAY(10), \ 133 MS_DASS(0xff), MS_DELAY(10), \ 134 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */), \ 135 MS_DASS(0x87), MS_DELAY(10), \ 136 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */), \ 137 MS_DASS(0x78), MS_DELAY(10), \ 138 MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */), \ 139 MS_DASS(MS_UNKNOWN /* param */), \ 140 MS_DELAY(2), \ 141 MS_CASS(0x0c), MS_DELAY(10), \ 142 MS_CASS(0x0d), MS_DELAY(2), \ 143 MS_CASS(0x0c), MS_DELAY(10), \ 144 MS_DASS(0xff), MS_DELAY(10), \ 145 MS_RET(0) \ 146} 147 148#define NEGOCIATED_MODE MS_PARAM(2, 1, MS_TYP_CHA) 149 150#define DECLARE_NEGOCIATE_MICROSEQ \ 151struct ppb_microseq negociate_microseq[] = { \ 152 MS_CASS(0x4), \ 153 MS_DELAY(5), \ 154 MS_DASS(MS_UNKNOWN /* mode */), \ 155 MS_DELAY(100), \ 156 MS_CASS(0x6), \ 157 MS_DELAY(5), \ 158 MS_BRSET(0x20, 5 /* continue */), \ 159 MS_DELAY(5), \ 160 MS_CASS(0x7), \ 161 MS_DELAY(5), \ 162 MS_CASS(0x6), \ 163 MS_RET(VP0_ENEGOCIATE), \ 164/* continue: */ \ 165 MS_DELAY(5), \ 166 MS_CASS(0x7), \ 167 MS_DELAY(5), \ 168 MS_CASS(0x6), \ 169 MS_RET(0) \ 170} 171 172#define INB_NIBBLE_L MS_PARAM(3, 2, MS_TYP_PTR) 173#define INB_NIBBLE_H MS_PARAM(6, 2, MS_TYP_PTR) 174#define INB_NIBBLE_F MS_PARAM(9, 0, MS_TYP_FUN) 175#define INB_NIBBLE_P MS_PARAM(9, 1, MS_TYP_PTR) 176 177/* 178 * This is the sub-microseqence for MS_GET in NIBBLE mode 179 * Retrieve the two nibbles and call the C function to generate the character 180 * and store it in the buffer (see nibble_inbyte_hook()) 181 */ 182 183#define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \ 184struct ppb_microseq nibble_inbyte_submicroseq[] = { \ 185 MS_CASS(0x4), \ 186/* loop: */ \ 187 MS_CASS(0x6), \ 188 MS_DELAY(1), \ 189 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\ 190 MS_CASS(0x5), \ 191 MS_DELAY(1), \ 192 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\ 193 MS_CASS(0x4), \ 194 MS_DELAY(1), \ 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_RET(0) \ 199} 200 201static struct ppb_microseq reset_microseq[] = { 202 MS_CASS(0x04), 203 MS_DASS(0x40), 204 MS_DELAY(1), 205 MS_CASS(0x0c), 206 MS_CASS(0x0d), 207 MS_DELAY(50), 208 MS_CASS(0x0c), 209 MS_CASS(0x04), 210 MS_RET(0) 211}; 212 213/* 214 * nibble_inbyte_hook() 215 * 216 * Formats high and low nibble into a character 217 */ 218static int 219nibble_inbyte_hook (void *p, char *ptr) 220{ 221 struct vpo_nibble *s = (struct vpo_nibble *)p; 222 223 /* increment the buffer pointer */ 224 *ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 225 226 return (0); 227} 228 229/* 230 * This is the sub-microseqence for MS_GET in PS2 mode 231 */ 232static struct ppb_microseq ps2_inbyte_submicroseq[] = { 233 MS_CASS(0x4), 234 235/* loop: */ 236 MS_CASS(PCD | 0x6), 237 MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 238 MS_CASS(PCD | 0x5), 239 MS_DBRA(-4 /* loop */), 240 241 MS_RET(0) 242}; 243 244/* 245 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 246 */ 247static struct ppb_microseq spp_outbyte_submicroseq[] = { 248 MS_CASS(0x4), 249 250/* loop: */ 251 MS_RASSERT_P(1, MS_REG_DTR), 252 MS_CASS(0x5), 253 MS_DBRA(0), /* decrement counter */ 254 MS_RASSERT_P(1, MS_REG_DTR), 255 MS_CASS(0x0), 256 MS_DBRA(-6 /* loop */), 257 258 /* return from the put call */ 259 MS_CASS(0x4), 260 MS_RET(0) 261}; 262 263/* EPP 1.7 microsequences, ptr and len set at runtime */ 264static struct ppb_microseq epp17_outstr[] = { 265 MS_CASS(0x4), 266 MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D), 267 MS_CASS(0xc), 268 MS_RET(0), 269}; 270 271static struct ppb_microseq epp17_instr[] = { 272 MS_CASS(PCD | 0x4), 273 MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL), 274 MS_CASS(PCD | 0xc), 275 MS_RET(0), 276}; 277 278static int 279imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus) 280{ 281 DECLARE_CPP_MICROSEQ; 282 283 device_t ppbus = device_get_parent(vpo->vpo_dev); 284 char s1, s2, s3; 285 int ret; 286 287 /* all should be ok */ 288 if (connected) 289 *connected = 0; 290 291 ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1, 292 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3, 293 CPP_PARAM, 0x30); 294 295 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 296 297 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) { 298 if (bootverbose) 299 printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n", 300 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 301 if (connected) 302 *connected = VP0_ECONNECT; 303 } 304 305 if (release_bus) 306 return (ppb_release_bus(ppbus, vpo->vpo_dev)); 307 else 308 return (0); 309} 310 311/* 312 * how : PPB_WAIT or PPB_DONTWAIT 313 */ 314static int 315imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus) 316{ 317 DECLARE_CPP_MICROSEQ; 318 319 device_t ppbus = device_get_parent(vpo->vpo_dev); 320 char s1, s2, s3; 321 int error; 322 int ret; 323 324 /* all should be ok */ 325 if (disconnected) 326 *disconnected = 0; 327 328 if (request_bus) 329 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) 330 return (error); 331 332 ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1, 333 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3); 334 335 /* select device 0 in compatible mode */ 336 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 337 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 338 339 /* disconnect all devices */ 340 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30); 341 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 342 343 if (PPB_IN_EPP_MODE(ppbus)) 344 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28); 345 else 346 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 347 348 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 349 350 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) { 351 if (bootverbose) 352 printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n", 353 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 354 if (disconnected) 355 *disconnected = VP0_ECONNECT; 356 } 357 358 return (0); 359} 360 361/* 362 * imm_detect() 363 * 364 * Detect and initialise the VP0 adapter. 365 */ 366static int 367imm_detect(struct vpoio_data *vpo) 368{ 369 device_t ppbus = device_get_parent(vpo->vpo_dev); 370 int error; 371 372 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT))) 373 return (error); 374 375 /* disconnect the drive, keep the bus */ 376 imm_disconnect(vpo, NULL, 0); 377 378 vpo->vpo_mode_found = VP0_MODE_UNDEFINED; 379 error = 1; 380 381 /* try to enter EPP mode since vpoio failure put the bus in NIBBLE */ 382 if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 383 imm_connect(vpo, PPB_DONTWAIT, &error, 0); 384 } 385 386 /* if connection failed try PS/2 then NIBBLE modes */ 387 if (error) { 388 if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 389 imm_connect(vpo, PPB_DONTWAIT, &error, 0); 390 } 391 if (error) { 392 if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) { 393 imm_connect(vpo, PPB_DONTWAIT, &error, 0); 394 if (error) 395 goto error; 396 vpo->vpo_mode_found = VP0_MODE_NIBBLE; 397 } else { 398 printf("imm%d: NIBBLE mode unavailable!\n", vpo->vpo_unit); 399 goto error; 400 } 401 } else { 402 vpo->vpo_mode_found = VP0_MODE_PS2; 403 } 404 } else { 405 vpo->vpo_mode_found = VP0_MODE_EPP; 406 } 407 408 /* send SCSI reset signal */ 409 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 410 411 /* release the bus now */ 412 imm_disconnect(vpo, &error, 1); 413 414 /* ensure we are disconnected or daisy chained peripheral 415 * may cause serious problem to the disk */ 416 417 if (error) { 418 if (bootverbose) 419 printf("imm%d: can't disconnect from the drive\n", 420 vpo->vpo_unit); 421 goto error; 422 } 423 424 return (0); 425 426error: 427 ppb_release_bus(ppbus, vpo->vpo_dev); 428 return (VP0_EINITFAILED); 429} 430 431/* 432 * imm_outstr() 433 */ 434static int 435imm_outstr(struct vpoio_data *vpo, char *buffer, int size) 436{ 437 device_t ppbus = device_get_parent(vpo->vpo_dev); 438 int error = 0; 439 440 if (PPB_IN_EPP_MODE(ppbus)) 441 ppb_reset_epp_timeout(ppbus); 442 443 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 444 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 445 446 return (error); 447} 448 449/* 450 * imm_instr() 451 */ 452static int 453imm_instr(struct vpoio_data *vpo, char *buffer, int size) 454{ 455 device_t ppbus = device_get_parent(vpo->vpo_dev); 456 int error = 0; 457 458 if (PPB_IN_EPP_MODE(ppbus)) 459 ppb_reset_epp_timeout(ppbus); 460 461 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 462 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 463 464 return (error); 465} 466 467static char 468imm_select(struct vpoio_data *vpo, int initiator, int target) 469{ 470 DECLARE_SELECT_MICROSEQUENCE; 471 device_t ppbus = device_get_parent(vpo->vpo_dev); 472 int ret; 473 474 /* initialize the select microsequence */ 475 ppb_MS_init_msq(select_microseq, 1, 476 SELECT_TARGET, 1 << initiator | 1 << target); 477 478 ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 479 480 return (ret); 481} 482 483/* 484 * imm_wait() 485 * 486 * H_SELIN must be low. 487 * 488 */ 489static char 490imm_wait(struct vpoio_data *vpo, int tmo) 491{ 492 DECLARE_WAIT_MICROSEQUENCE; 493 494 device_t ppbus = device_get_parent(vpo->vpo_dev); 495 int ret, err; 496 497 /* 498 * Return some status information. 499 * Semantics : 0x88 = ZIP+ wants more data 500 * 0x98 = ZIP+ wants to send more data 501 * 0xa8 = ZIP+ wants command 502 * 0xb8 = end of transfer, ZIP+ is sending status 503 */ 504 505 ppb_MS_init_msq(wait_microseq, 2, 506 WAIT_RET, (void *)&ret, 507 WAIT_TMO, tmo); 508 509 ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err); 510 511 if (err) 512 return (0); /* command timed out */ 513 514 return(ret); 515} 516 517static int 518imm_negociate(struct vpoio_data *vpo) 519{ 520 DECLARE_NEGOCIATE_MICROSEQ; 521 device_t ppbus = device_get_parent(vpo->vpo_dev); 522 int negociate_mode; 523 int ret; 524 525 if (PPB_IN_NIBBLE_MODE(ppbus)) 526 negociate_mode = 0; 527 else if (PPB_IN_PS2_MODE(ppbus)) 528 negociate_mode = 1; 529 else 530 return (0); 531 532#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */ 533 ret = ppb_1284_negociate(ppbus, negociate_mode); 534 535 if (ret) 536 return (VP0_ENEGOCIATE); 537#endif 538 539 ppb_MS_init_msq(negociate_microseq, 1, 540 NEGOCIATED_MODE, negociate_mode); 541 542 ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret); 543 544 return (ret); 545} 546 547/* 548 * imm_probe() 549 * 550 * Low level probe of vpo device 551 * 552 */ 553int 554imm_probe(device_t dev, struct vpoio_data *vpo) 555{ 556 int error; 557 558 /* ppbus dependent initialisation */ 559 vpo->vpo_dev = dev; 560 561 /* now, try to initialise the drive */ 562 if ((error = imm_detect(vpo))) { 563 return (error); 564 } 565 566 return (0); 567} 568 569/* 570 * imm_attach() 571 * 572 * Low level attachment of vpo device 573 * 574 */ 575int 576imm_attach(struct vpoio_data *vpo) 577{ 578 DECLARE_NIBBLE_INBYTE_SUBMICROSEQ; 579 device_t ppbus = device_get_parent(vpo->vpo_dev); 580 int error = 0; 581 582 /* 583 * Initialize microsequence code 584 */ 585 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 586 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 587 588 if (!vpo->vpo_nibble_inbyte_msq) 589 return (ENXIO); 590 591 bcopy((void *)nibble_inbyte_submicroseq, 592 (void *)vpo->vpo_nibble_inbyte_msq, 593 sizeof(nibble_inbyte_submicroseq)); 594 595 ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4, 596 INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h, 597 INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l, 598 INB_NIBBLE_F, nibble_inbyte_hook, 599 INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 600 601 /* 602 * Initialize mode dependent in/out microsequences 603 */ 604 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT))) 605 goto error; 606 607 /* ppbus automatically restore the last mode entered during detection */ 608 switch (vpo->vpo_mode_found) { 609 case VP0_MODE_EPP: 610 ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr); 611 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr); 612 printf("imm%d: EPP mode\n", vpo->vpo_unit); 613 break; 614 case VP0_MODE_PS2: 615 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 616 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 617 printf("imm%d: PS2 mode\n", vpo->vpo_unit); 618 break; 619 case VP0_MODE_NIBBLE: 620 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 621 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 622 printf("imm%d: NIBBLE mode\n", vpo->vpo_unit); 623 break; 624 default: 625 panic("imm: unknown mode %d", vpo->vpo_mode_found); 626 } 627 628 ppb_release_bus(ppbus, vpo->vpo_dev); 629 error: 630 return (error); 631} 632 633/* 634 * imm_reset_bus() 635 * 636 */ 637int 638imm_reset_bus(struct vpoio_data *vpo) 639{ 640 device_t ppbus = device_get_parent(vpo->vpo_dev); 641 int disconnected; 642 643 /* first, connect to the drive and request the bus */ 644 imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1); 645 646 if (!disconnected) { 647 648 /* reset the SCSI bus */ 649 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 650 651 /* then disconnect */ 652 imm_disconnect(vpo, NULL, 1); 653 } 654 655 return (0); 656} 657 658/* 659 * imm_do_scsi() 660 * 661 * Send an SCSI command 662 * 663 */ 664int 665imm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 666 int clen, char *buffer, int blen, int *result, int *count, 667 int *ret) 668{ 669 device_t ppbus = device_get_parent(vpo->vpo_dev); 670 register char r; 671 char l, h = 0; 672 int len, error = 0, not_connected = 0; 673 register int k; 674 int negociated = 0; 675 676 /* 677 * enter disk state, allocate the ppbus 678 * 679 * XXX 680 * Should we allow this call to be interruptible? 681 * The only way to report the interruption is to return 682 * EIO to upper SCSI code :^( 683 */ 684 if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, ¬_connected, 1))) 685 return (error); 686 687 if (not_connected) { 688 *ret = VP0_ECONNECT; goto error; 689 } 690 691 /* 692 * Select the drive ... 693 */ 694 if ((*ret = imm_select(vpo,host,target))) 695 goto error; 696 697 /* 698 * Send the command ... 699 */ 700 for (k = 0; k < clen; k+=2) { 701 if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) { 702 *ret = VP0_ECMD_TIMEOUT; 703 goto error; 704 } 705 if (imm_outstr(vpo, &command[k], 2)) { 706 *ret = VP0_EPPDATA_TIMEOUT; 707 goto error; 708 } 709 } 710 711 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 712 *ret = VP0_ESTATUS_TIMEOUT; goto error; 713 } 714 715 if ((r & 0x30) == 0x10) { 716 if (imm_negociate(vpo)) { 717 *ret = VP0_ENEGOCIATE; 718 goto error; 719 } else 720 negociated = 1; 721 } 722 723 /* 724 * Complete transfer ... 725 */ 726 *count = 0; 727 for (;;) { 728 729 if (!(r = imm_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)0xb8) 735 break; 736 737 if (*count >= blen) { 738 *ret = VP0_EDATA_OVERFLOW; 739 goto error; 740 } 741 742 /* ZIP+ wants to send data? */ 743 if (r == (char)0x88) { 744 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 745 VP0_SECTOR_SIZE : 2; 746 747 error = imm_outstr(vpo, &buffer[*count], len); 748 } else { 749 if (!PPB_IN_EPP_MODE(ppbus)) 750 len = 1; 751 else 752 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 753 VP0_SECTOR_SIZE : 1; 754 755 error = imm_instr(vpo, &buffer[*count], len); 756 } 757 758 if (error) { 759 *ret = error; 760 goto error; 761 } 762 763 *count += len; 764 } 765 766 if ((PPB_IN_NIBBLE_MODE(ppbus) || 767 PPB_IN_PS2_MODE(ppbus)) && negociated) 768 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 769 770 /* 771 * Retrieve status ... 772 */ 773 if (imm_negociate(vpo)) { 774 *ret = VP0_ENEGOCIATE; 775 goto error; 776 } else 777 negociated = 1; 778 779 if (imm_instr(vpo, &l, 1)) { 780 *ret = VP0_EOTHER; goto error; 781 } 782 783 /* check if the ZIP+ wants to send more status */ 784 if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8) 785 if (imm_instr(vpo, &h, 1)) { 786 *ret = VP0_EOTHER+2; goto error; 787 } 788 789 /* Experience showed that we should discard this */ 790 if (h == -1) 791 h = 0; 792 793 *result = ((int) h << 8) | ((int) l & 0xff); 794 795error: 796 if ((PPB_IN_NIBBLE_MODE(ppbus) || 797 PPB_IN_PS2_MODE(ppbus)) && negociated) 798 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 799 800 /* return to printer state, release the ppbus */ 801 imm_disconnect(vpo, NULL, 1); 802 803 return (0); 804} 805