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