immio.c revision 59368
1326949Sdim/*- 2326949Sdim * Copyright (c) 1998, 1999 Nicolas Souchu 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6326949Sdim * modification, are permitted provided that the following conditions 7326949Sdim * are met: 8326949Sdim * 1. Redistributions of source code must retain the above copyright 9326949Sdim * notice, this list of conditions and the following disclaimer. 10326949Sdim * 2. Redistributions in binary form must reproduce the above copyright 11326949Sdim * notice, this list of conditions and the following disclaimer in the 12326949Sdim * documentation and/or other materials provided with the distribution. 13344779Sdim * 14326949Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15326949Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16326949Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17326949Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18353358Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19353358Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20326949Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21326949Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22326949Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23326949Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24326949Sdim * SUCH DAMAGE. 25326949Sdim * 26353358Sdim * $FreeBSD: head/sys/dev/ppbus/immio.c 59368 2000-04-18 15:15:39Z phk $ 27353358Sdim * 28326949Sdim */ 29326949Sdim 30326949Sdim/* 31326949Sdim * Iomega ZIP+ Matchmaker Parallel Port Interface driver 32326949Sdim * 33353358Sdim * Thanks to David Campbell work on the Linux driver and the Iomega specs 34353358Sdim * Thanks to Thiebault Moeglin for the drive 35326949Sdim */ 36326949Sdim#ifdef _KERNEL 37326949Sdim#include <sys/param.h> 38326949Sdim#include <sys/systm.h> 39353358Sdim#include <sys/module.h> 40353358Sdim#include <sys/bus.h> 41326949Sdim#include <sys/malloc.h> 42326949Sdim 43326949Sdim#include <machine/clock.h> 44326949Sdim 45353358Sdim#endif /* _KERNEL */ 46353358Sdim 47326949Sdim#ifdef _KERNEL 48326949Sdim#include <sys/kernel.h> 49326949Sdim#endif /* _KERNEL */ 50326949Sdim 51326949Sdim#include "opt_vpo.h" 52326949Sdim 53326949Sdim#include <dev/ppbus/ppbio.h> 54326949Sdim#include <dev/ppbus/ppbconf.h> 55326949Sdim#include <dev/ppbus/ppb_msq.h> 56326949Sdim#include <dev/ppbus/vpoio.h> 57326949Sdim#include <dev/ppbus/ppb_1284.h> 58326949Sdim 59326949Sdim#include "ppbus_if.h" 60326949Sdim 61326949Sdim#define VP0_SELTMO 5000 /* select timeout */ 62326949Sdim#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 63326949Sdim#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 64326949Sdim 65326949Sdim#define VP0_SECTOR_SIZE 512 66326949Sdim 67326949Sdim/* 68326949Sdim * Microcode to execute very fast I/O sequences at the lowest bus level. 69326949Sdim */ 70326949Sdim 71326949Sdim#define SELECT_TARGET MS_PARAM(6, 1, MS_TYP_CHA) 72326949Sdim 73326949Sdim#define DECLARE_SELECT_MICROSEQUENCE \ 74326949Sdimstruct ppb_microseq select_microseq[] = { \ 75326949Sdim MS_CASS(0xc), \ 76326949Sdim /* first, check there is nothing holding onto the bus */ \ 77326949Sdim MS_SET(VP0_SELTMO), \ 78326949Sdim/* _loop: */ \ 79326949Sdim MS_BRCLEAR(0x8, 2 /* _ready */), \ 80326949Sdim MS_DBRA(-2 /* _loop */), \ 81326949Sdim MS_RET(2), /* bus busy */ \ 82326949Sdim/* _ready: */ \ 83326949Sdim MS_CASS(0x4), \ 84326949Sdim MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */), \ 85326949Sdim MS_DELAY(1), \ 86326949Sdim MS_CASS(0xc), \ 87326949Sdim MS_CASS(0xd), \ 88326949Sdim /* now, wait until the drive is ready */ \ 89326949Sdim MS_SET(VP0_SELTMO), \ 90326949Sdim/* loop: */ \ 91326949Sdim MS_BRSET(0x8, 3 /* ready */), \ 92326949Sdim MS_DBRA(-2 /* loop */), \ 93326949Sdim/* error: */ \ 94326949Sdim MS_CASS(0xc), \ 95326949Sdim MS_RET(VP0_ESELECT_TIMEOUT), \ 96326949Sdim/* ready: */ \ 97326949Sdim MS_CASS(0xc), \ 98326949Sdim MS_RET(0) \ 99326949Sdim} 100326949Sdim 101326949Sdimstatic struct ppb_microseq transfer_epilog[] = { 102326949Sdim MS_CASS(0x4), 103326949Sdim MS_CASS(0xc), 104326949Sdim MS_CASS(0xe), 105326949Sdim MS_CASS(0x4), 106326949Sdim MS_RET(0) 107326949Sdim}; 108326949Sdim 109326949Sdim#define CPP_S1 MS_PARAM(10, 2, MS_TYP_PTR) 110326949Sdim#define CPP_S2 MS_PARAM(13, 2, MS_TYP_PTR) 111326949Sdim#define CPP_S3 MS_PARAM(16, 2, MS_TYP_PTR) 112326949Sdim#define CPP_PARAM MS_PARAM(17, 1, MS_TYP_CHA) 113326949Sdim 114326949Sdim#define DECLARE_CPP_MICROSEQ \ 115326949Sdimstruct ppb_microseq cpp_microseq[] = { \ 116326949Sdim MS_CASS(0x0c), MS_DELAY(2), \ 117326949Sdim MS_DASS(0xaa), MS_DELAY(10), \ 118326949Sdim MS_DASS(0x55), MS_DELAY(10), \ 119326949Sdim MS_DASS(0x00), MS_DELAY(10), \ 120326949Sdim MS_DASS(0xff), MS_DELAY(10), \ 121326949Sdim MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */), \ 122326949Sdim MS_DASS(0x87), MS_DELAY(10), \ 123326949Sdim MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */), \ 124326949Sdim MS_DASS(0x78), MS_DELAY(10), \ 125326949Sdim MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */), \ 126326949Sdim MS_DASS(MS_UNKNOWN /* param */), \ 127326949Sdim MS_DELAY(2), \ 128326949Sdim MS_CASS(0x0c), MS_DELAY(10), \ 129326949Sdim MS_CASS(0x0d), MS_DELAY(2), \ 130326949Sdim MS_CASS(0x0c), MS_DELAY(10), \ 131326949Sdim MS_DASS(0xff), MS_DELAY(10), \ 132326949Sdim MS_RET(0) \ 133326949Sdim} 134 135#define NEGOCIATED_MODE MS_PARAM(2, 1, MS_TYP_CHA) 136 137#define DECLARE_NEGOCIATE_MICROSEQ \ 138static struct ppb_microseq negociate_microseq[] = { \ 139 MS_CASS(0x4), \ 140 MS_DELAY(5), \ 141 MS_DASS(MS_UNKNOWN /* mode */), \ 142 MS_DELAY(100), \ 143 MS_CASS(0x6), \ 144 MS_DELAY(5), \ 145 MS_BRSET(0x20, 5 /* continue */), \ 146 MS_DELAY(5), \ 147 MS_CASS(0x7), \ 148 MS_DELAY(5), \ 149 MS_CASS(0x6), \ 150 MS_RET(VP0_ENEGOCIATE), \ 151/* continue: */ \ 152 MS_DELAY(5), \ 153 MS_CASS(0x7), \ 154 MS_DELAY(5), \ 155 MS_CASS(0x6), \ 156 MS_RET(0) \ 157} 158 159static struct ppb_microseq reset_microseq[] = { 160 MS_CASS(0x04), 161 MS_DASS(0x40), 162 MS_DELAY(1), 163 MS_CASS(0x0c), 164 MS_CASS(0x0d), 165 MS_DELAY(50), 166 MS_CASS(0x0c), 167 MS_CASS(0x04), 168 MS_RET(0) 169}; 170 171/* 172 * nibble_inbyte_hook() 173 * 174 * Formats high and low nibble into a character 175 */ 176static int 177nibble_inbyte_hook (void *p, char *ptr) 178{ 179 struct vpo_nibble *s = (struct vpo_nibble *)p; 180 181 /* increment the buffer pointer */ 182 *ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 183 184 return (0); 185} 186 187/* 188 * Macro used to initialize each vpoio_data structure during 189 * low level attachment 190 * 191 * XXX should be converted to ppb_MS_init_msq() 192 */ 193#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) { \ 194 (vpo)->vpo_nibble_inbyte_msq[6].arg[2].p = \ 195 (void *)&(vpo)->vpo_nibble.h; \ 196 (vpo)->vpo_nibble_inbyte_msq[3].arg[2].p = \ 197 (void *)&(vpo)->vpo_nibble.l; \ 198 (vpo)->vpo_nibble_inbyte_msq[9].arg[0].f = \ 199 nibble_inbyte_hook; \ 200 (vpo)->vpo_nibble_inbyte_msq[9].arg[1].p = \ 201 (void *)&(vpo)->vpo_nibble; \ 202} 203 204/* 205 * This is the sub-microseqence for MS_GET in NIBBLE mode 206 * Retrieve the two nibbles and call the C function to generate the character 207 * and store it in the buffer (see nibble_inbyte_hook()) 208 */ 209static struct ppb_microseq nibble_inbyte_submicroseq[] = { 210 MS_CASS(0x4), 211 212/* loop: */ 213 MS_CASS(0x6), 214 MS_DELAY(1), 215 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */), 216 MS_CASS(0x5), 217 MS_DELAY(1), 218 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */), 219 MS_CASS(0x4), 220 MS_DELAY(1), 221 222 /* do a C call to format the received nibbles */ 223 MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), 224 MS_DBRA(-7 /* loop */), 225 MS_RET(0) 226}; 227 228/* 229 * This is the sub-microseqence for MS_GET in PS2 mode 230 */ 231static struct ppb_microseq ps2_inbyte_submicroseq[] = { 232 MS_CASS(0x4), 233 234/* loop: */ 235 MS_CASS(PCD | 0x6), 236 MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 237 MS_CASS(PCD | 0x5), 238 MS_DBRA(-4 /* loop */), 239 240 MS_RET(0) 241}; 242 243/* 244 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 245 */ 246static struct ppb_microseq spp_outbyte_submicroseq[] = { 247 MS_CASS(0x4), 248 249/* loop: */ 250 MS_RASSERT_P(1, MS_REG_DTR), 251 MS_CASS(0x5), 252 MS_DBRA(0), /* decrement counter */ 253 MS_RASSERT_P(1, MS_REG_DTR), 254 MS_CASS(0x0), 255 MS_DBRA(-6 /* loop */), 256 257 /* return from the put call */ 258 MS_CASS(0x4), 259 MS_RET(0) 260}; 261 262/* EPP 1.7 microsequences, ptr and len set at runtime */ 263static struct ppb_microseq epp17_outstr[] = { 264 MS_CASS(0x4), 265 MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D), 266 MS_CASS(0xc), 267 MS_RET(0), 268}; 269 270static struct ppb_microseq epp17_instr[] = { 271 MS_CASS(PCD | 0x4), 272 MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL), 273 MS_CASS(PCD | 0xc), 274 MS_RET(0), 275}; 276 277static int 278imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus) 279{ 280 DECLARE_CPP_MICROSEQ; 281 282 device_t ppbus = device_get_parent(vpo->vpo_dev); 283 char s1, s2, s3; 284 int ret; 285 286 /* all should be ok */ 287 if (connected) 288 *connected = 0; 289 290 ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1, 291 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3, 292 CPP_PARAM, 0x30); 293 294 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 295 296 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) { 297 if (bootverbose) 298 printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n", 299 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 300 if (connected) 301 *connected = VP0_ECONNECT; 302 } 303 304 if (release_bus) 305 return (ppb_release_bus(ppbus, vpo->vpo_dev)); 306 else 307 return (0); 308} 309 310/* 311 * how : PPB_WAIT or PPB_DONTWAIT 312 */ 313static int 314imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus) 315{ 316 DECLARE_CPP_MICROSEQ; 317 318 device_t ppbus = device_get_parent(vpo->vpo_dev); 319 char s1, s2, s3; 320 int error; 321 int ret; 322 323 /* all should be ok */ 324 if (disconnected) 325 *disconnected = 0; 326 327 if (request_bus) 328 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) 329 return (error); 330 331 ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1, 332 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3); 333 334 /* select device 0 in compatible mode */ 335 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 336 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 337 338 /* disconnect all devices */ 339 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30); 340 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 341 342 if (PPB_IN_EPP_MODE(ppbus)) 343 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28); 344 else 345 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 346 347 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 348 349 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) { 350 if (bootverbose) 351 printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n", 352 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 353 if (disconnected) 354 *disconnected = VP0_ECONNECT; 355 } 356 357 return (0); 358} 359 360/* 361 * imm_detect() 362 * 363 * Detect and initialise the VP0 adapter. 364 */ 365static int 366imm_detect(struct vpoio_data *vpo) 367{ 368 device_t ppbus = device_get_parent(vpo->vpo_dev); 369 int error; 370 371 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT))) 372 return (error); 373 374 /* disconnect the drive, keep the bus */ 375 imm_disconnect(vpo, NULL, 0); 376 377 /* we already have the bus, just connect */ 378 imm_connect(vpo, PPB_DONTWAIT, &error, 0); 379 380 if (error) { 381 if (bootverbose) 382 printf("imm%d: can't connect to the drive\n", 383 vpo->vpo_unit); 384 goto error; 385 } 386 387 /* send SCSI reset signal */ 388 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 389 390 /* release the bus now */ 391 imm_disconnect(vpo, &error, 1); 392 393 /* ensure we are disconnected or daisy chained peripheral 394 * may cause serious problem to the disk */ 395 396 if (error) { 397 if (bootverbose) 398 printf("imm%d: can't disconnect from the drive\n", 399 vpo->vpo_unit); 400 goto error; 401 } 402 403 return (0); 404 405error: 406 ppb_release_bus(ppbus, vpo->vpo_dev); 407 return (VP0_EINITFAILED); 408} 409 410/* 411 * imm_outstr() 412 */ 413static int 414imm_outstr(struct vpoio_data *vpo, char *buffer, int size) 415{ 416 device_t ppbus = device_get_parent(vpo->vpo_dev); 417 int error = 0; 418 419 if (PPB_IN_EPP_MODE(ppbus)) 420 ppb_reset_epp_timeout(ppbus); 421 422 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 423 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 424 425 return (error); 426} 427 428/* 429 * imm_instr() 430 */ 431static int 432imm_instr(struct vpoio_data *vpo, char *buffer, int size) 433{ 434 device_t ppbus = device_get_parent(vpo->vpo_dev); 435 int error = 0; 436 437 if (PPB_IN_EPP_MODE(ppbus)) 438 ppb_reset_epp_timeout(ppbus); 439 440 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 441 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 442 443 return (error); 444} 445 446static char 447imm_select(struct vpoio_data *vpo, int initiator, int target) 448{ 449 DECLARE_SELECT_MICROSEQUENCE; 450 device_t ppbus = device_get_parent(vpo->vpo_dev); 451 int ret; 452 453 /* initialize the select microsequence */ 454 ppb_MS_init_msq(select_microseq, 1, 455 SELECT_TARGET, 1 << initiator | 1 << target); 456 457 ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 458 459 return (ret); 460} 461 462/* 463 * imm_wait() 464 * 465 * H_SELIN must be low. 466 * 467 * XXX should be ported to microseq 468 */ 469static char 470imm_wait(struct vpoio_data *vpo, int tmo) 471{ 472 device_t ppbus = device_get_parent(vpo->vpo_dev); 473 register int k; 474 register char r; 475 476 ppb_wctr(ppbus, 0xc); 477 478 /* XXX should be ported to microseq */ 479 k = 0; 480 while (!((r = ppb_rstr(ppbus)) & 0x80) && (k++ < tmo)) 481 DELAY(1); 482 483 /* 484 * Return some status information. 485 * Semantics : 0x88 = ZIP+ wants more data 486 * 0x98 = ZIP+ wants to send more data 487 * 0xa8 = ZIP+ wants command 488 * 0xb8 = end of transfer, ZIP+ is sending status 489 */ 490 ppb_wctr(ppbus, 0x4); 491 if (k < tmo) 492 return (r & 0xb8); 493 494 return (0); /* command timed out */ 495} 496 497static int 498imm_negociate(struct vpoio_data *vpo) 499{ 500 DECLARE_NEGOCIATE_MICROSEQ; 501 device_t ppbus = device_get_parent(vpo->vpo_dev); 502 int negociate_mode; 503 int ret; 504 505 if (PPB_IN_NIBBLE_MODE(ppbus)) 506 negociate_mode = 0; 507 else if (PPB_IN_PS2_MODE(ppbus)) 508 negociate_mode = 1; 509 else 510 return (0); 511 512#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */ 513 ret = ppb_1284_negociate(ppbus, negociate_mode); 514 515 if (ret) 516 return (VP0_ENEGOCIATE); 517#endif 518 519 ppb_MS_init_msq(negociate_microseq, 1, 520 NEGOCIATED_MODE, negociate_mode); 521 522 ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret); 523 524 return (ret); 525} 526 527/* 528 * imm_probe() 529 * 530 * Low level probe of vpo device 531 * 532 */ 533int 534imm_probe(device_t dev, struct vpoio_data *vpo) 535{ 536 int error; 537 538 /* ppbus dependent initialisation */ 539 vpo->vpo_dev = dev; 540 541 /* now, try to initialise the drive */ 542 if ((error = imm_detect(vpo))) { 543 return (error); 544 } 545 546 return (0); 547} 548 549/* 550 * imm_attach() 551 * 552 * Low level attachment of vpo device 553 * 554 */ 555int 556imm_attach(struct vpoio_data *vpo) 557{ 558 device_t ppbus = device_get_parent(vpo->vpo_dev); 559 int epp; 560 561 /* 562 * Initialize microsequence code 563 */ 564 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 565 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 566 567 if (!vpo->vpo_nibble_inbyte_msq) 568 return (ENXIO); 569 570 bcopy((void *)nibble_inbyte_submicroseq, 571 (void *)vpo->vpo_nibble_inbyte_msq, 572 sizeof(nibble_inbyte_submicroseq)); 573 574 INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo); 575 576 /* 577 * Initialize mode dependent in/out microsequences 578 */ 579 ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT); 580 581 /* enter NIBBLE mode to configure submsq */ 582 if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) { 583 584 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 585 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 586 } 587 588 /* enter PS2 mode to configure submsq */ 589 if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 590 591 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 592 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 593 } 594 595 epp = ppb_get_epp_protocol(ppbus); 596 597 /* enter EPP mode to configure submsq */ 598 if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 599 600 switch (epp) { 601 case EPP_1_9: 602 case EPP_1_7: 603 ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr); 604 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr); 605 break; 606 default: 607 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 608 epp); 609 } 610 } 611 612 /* try to enter EPP or PS/2 mode, NIBBLE otherwise */ 613 if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 614 switch (epp) { 615 case EPP_1_9: 616 printf("imm%d: EPP 1.9 mode\n", vpo->vpo_unit); 617 break; 618 case EPP_1_7: 619 printf("imm%d: EPP 1.7 mode\n", vpo->vpo_unit); 620 break; 621 default: 622 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 623 epp); 624 } 625 } else if (ppb_set_mode(ppbus, PPB_PS2) != -1) 626 printf("imm%d: PS2 mode\n", vpo->vpo_unit); 627 628 else if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) 629 printf("imm%d: NIBBLE mode\n", vpo->vpo_unit); 630 631 else { 632 printf("imm%d: can't enter NIBBLE, PS2 or EPP mode\n", 633 vpo->vpo_unit); 634 635 ppb_release_bus(ppbus, vpo->vpo_dev); 636 637 free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF); 638 return (ENXIO); 639 } 640 641 ppb_release_bus(ppbus, vpo->vpo_dev); 642 643 return (0); 644} 645 646/* 647 * imm_reset_bus() 648 * 649 */ 650int 651imm_reset_bus(struct vpoio_data *vpo) 652{ 653 device_t ppbus = device_get_parent(vpo->vpo_dev); 654 int disconnected; 655 656 /* first, connect to the drive and request the bus */ 657 imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1); 658 659 if (!disconnected) { 660 661 /* reset the SCSI bus */ 662 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 663 664 /* then disconnect */ 665 imm_disconnect(vpo, NULL, 1); 666 } 667 668 return (0); 669} 670 671/* 672 * imm_do_scsi() 673 * 674 * Send an SCSI command 675 * 676 */ 677int 678imm_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 register char r; 684 char l, h = 0; 685 int len, error = 0, not_connected = 0; 686 register int k; 687 int negociated = 0; 688 689 /* 690 * enter disk state, allocate the ppbus 691 * 692 * XXX 693 * Should we allow this call to be interruptible? 694 * The only way to report the interruption is to return 695 * EIO do upper SCSI code :^( 696 */ 697 if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, ¬_connected, 1))) 698 return (error); 699 700 if (not_connected) { 701 *ret = VP0_ECONNECT; goto error; 702 } 703 704 /* 705 * Select the drive ... 706 */ 707 if ((*ret = imm_select(vpo,host,target))) 708 goto error; 709 710 /* 711 * Send the command ... 712 */ 713 for (k = 0; k < clen; k+=2) { 714 if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) { 715 *ret = VP0_ECMD_TIMEOUT; 716 goto error; 717 } 718 if (imm_outstr(vpo, &command[k], 2)) { 719 *ret = VP0_EPPDATA_TIMEOUT; 720 goto error; 721 } 722 } 723 724 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 725 *ret = VP0_ESTATUS_TIMEOUT; goto error; 726 } 727 728 if ((r & 0x30) == 0x10) { 729 if (imm_negociate(vpo)) { 730 *ret = VP0_ENEGOCIATE; 731 goto error; 732 } else 733 negociated = 1; 734 } 735 736 /* 737 * Complete transfer ... 738 */ 739 *count = 0; 740 for (;;) { 741 742 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 743 *ret = VP0_ESTATUS_TIMEOUT; goto error; 744 } 745 746 /* stop when the ZIP+ wants to send status */ 747 if (r == (char)0xb8) 748 break; 749 750 if (*count >= blen) { 751 *ret = VP0_EDATA_OVERFLOW; 752 goto error; 753 } 754 755 /* ZIP+ wants to send data? */ 756 if (r == (char)0x88) { 757 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 758 VP0_SECTOR_SIZE : 2; 759 760 error = imm_outstr(vpo, &buffer[*count], len); 761 } else { 762 if (!PPB_IN_EPP_MODE(ppbus)) 763 len = 1; 764 else 765 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 766 VP0_SECTOR_SIZE : 1; 767 768 error = imm_instr(vpo, &buffer[*count], len); 769 } 770 771 if (error) { 772 *ret = error; 773 goto error; 774 } 775 776 *count += len; 777 } 778 779 if ((PPB_IN_NIBBLE_MODE(ppbus) || 780 PPB_IN_PS2_MODE(ppbus)) && negociated) 781 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 782 783 /* 784 * Retrieve status ... 785 */ 786 if (imm_negociate(vpo)) { 787 *ret = VP0_ENEGOCIATE; 788 goto error; 789 } else 790 negociated = 1; 791 792 if (imm_instr(vpo, &l, 1)) { 793 *ret = VP0_EOTHER; goto error; 794 } 795 796 /* check if the ZIP+ wants to send more status */ 797 if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8) 798 if (imm_instr(vpo, &h, 1)) { 799 *ret = VP0_EOTHER+2; goto error; 800 } 801 802 *result = ((int) h << 8) | ((int) l & 0xff); 803 804error: 805 if ((PPB_IN_NIBBLE_MODE(ppbus) || 806 PPB_IN_PS2_MODE(ppbus)) && negociated) 807 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 808 809 /* return to printer state, release the ppbus */ 810 imm_disconnect(vpo, NULL, 1); 811 812 return (0); 813} 814