immio.c revision 67164
1169689Skan/*- 2169689Skan * Copyright (c) 1998, 1999 Nicolas Souchu 3169689Skan * All rights reserved. 4169689Skan * 5169689Skan * Redistribution and use in source and binary forms, with or without 6169689Skan * modification, are permitted provided that the following conditions 7169689Skan * are met: 8169689Skan * 1. Redistributions of source code must retain the above copyright 9169689Skan * notice, this list of conditions and the following disclaimer. 10169689Skan * 2. Redistributions in binary form must reproduce the above copyright 11169689Skan * notice, this list of conditions and the following disclaimer in the 12169689Skan * documentation and/or other materials provided with the distribution. 13169689Skan * 14169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24169689Skan * SUCH DAMAGE. 25169689Skan * 26169689Skan * $FreeBSD: head/sys/dev/ppbus/immio.c 67164 2000-10-15 14:19:01Z phk $ 27169689Skan * 28169689Skan */ 29169689Skan 30169689Skan/* 31169689Skan * Iomega ZIP+ Matchmaker Parallel Port Interface driver 32169689Skan * 33169689Skan * Thanks to David Campbell work on the Linux driver and the Iomega specs 34169689Skan * Thanks to Thiebault Moeglin for the drive 35169689Skan */ 36169689Skan#ifdef _KERNEL 37169689Skan#include <sys/param.h> 38169689Skan#include <sys/systm.h> 39169689Skan#include <sys/module.h> 40169689Skan#include <sys/bus.h> 41169689Skan#include <sys/malloc.h> 42169689Skan 43169689Skan 44169689Skan#endif /* _KERNEL */ 45169689Skan 46169689Skan#ifdef _KERNEL 47169689Skan#endif /* _KERNEL */ 48169689Skan 49169689Skan#include "opt_vpo.h" 50169689Skan 51169689Skan#include <dev/ppbus/ppbio.h> 52169689Skan#include <dev/ppbus/ppbconf.h> 53169689Skan#include <dev/ppbus/ppb_msq.h> 54169689Skan#include <dev/ppbus/vpoio.h> 55169689Skan#include <dev/ppbus/ppb_1284.h> 56169689Skan 57169689Skan#include "ppbus_if.h" 58169689Skan 59169689Skan#define VP0_SELTMO 5000 /* select timeout */ 60169689Skan#define VP0_FAST_SPINTMO 500000 /* wait status timeout */ 61169689Skan#define VP0_LOW_SPINTMO 5000000 /* wait status timeout */ 62169689Skan 63169689Skan#define VP0_SECTOR_SIZE 512 64169689Skan 65169689Skan/* 66169689Skan * Microcode to execute very fast I/O sequences at the lowest bus level. 67169689Skan */ 68169689Skan 69169689Skan#define SELECT_TARGET MS_PARAM(6, 1, MS_TYP_CHA) 70169689Skan 71169689Skan#define DECLARE_SELECT_MICROSEQUENCE \ 72169689Skanstruct ppb_microseq select_microseq[] = { \ 73169689Skan MS_CASS(0xc), \ 74169689Skan /* first, check there is nothing holding onto the bus */ \ 75169689Skan MS_SET(VP0_SELTMO), \ 76169689Skan/* _loop: */ \ 77169689Skan MS_BRCLEAR(0x8, 2 /* _ready */), \ 78169689Skan MS_DBRA(-2 /* _loop */), \ 79169689Skan MS_RET(2), /* bus busy */ \ 80169689Skan/* _ready: */ \ 81169689Skan MS_CASS(0x4), \ 82169689Skan MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */), \ 83169689Skan MS_DELAY(1), \ 84169689Skan MS_CASS(0xc), \ 85169689Skan MS_CASS(0xd), \ 86169689Skan /* now, wait until the drive is ready */ \ 87169689Skan MS_SET(VP0_SELTMO), \ 88169689Skan/* loop: */ \ 89169689Skan MS_BRSET(0x8, 3 /* ready */), \ 90169689Skan MS_DBRA(-2 /* loop */), \ 91169689Skan/* error: */ \ 92169689Skan MS_CASS(0xc), \ 93169689Skan MS_RET(VP0_ESELECT_TIMEOUT), \ 94169689Skan/* ready: */ \ 95169689Skan MS_CASS(0xc), \ 96169689Skan MS_RET(0) \ 97169689Skan} 98169689Skan 99169689Skanstatic struct ppb_microseq transfer_epilog[] = { 100169689Skan MS_CASS(0x4), 101169689Skan MS_CASS(0xc), 102169689Skan MS_CASS(0xe), 103169689Skan MS_CASS(0x4), 104169689Skan MS_RET(0) 105169689Skan}; 106169689Skan 107169689Skan#define CPP_S1 MS_PARAM(10, 2, MS_TYP_PTR) 108169689Skan#define CPP_S2 MS_PARAM(13, 2, MS_TYP_PTR) 109169689Skan#define CPP_S3 MS_PARAM(16, 2, MS_TYP_PTR) 110169689Skan#define CPP_PARAM MS_PARAM(17, 1, MS_TYP_CHA) 111169689Skan 112169689Skan#define DECLARE_CPP_MICROSEQ \ 113169689Skanstruct ppb_microseq cpp_microseq[] = { \ 114169689Skan MS_CASS(0x0c), MS_DELAY(2), \ 115169689Skan MS_DASS(0xaa), MS_DELAY(10), \ 116169689Skan MS_DASS(0x55), MS_DELAY(10), \ 117169689Skan MS_DASS(0x00), MS_DELAY(10), \ 118169689Skan MS_DASS(0xff), MS_DELAY(10), \ 119169689Skan MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */), \ 120169689Skan MS_DASS(0x87), MS_DELAY(10), \ 121169689Skan MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */), \ 122169689Skan MS_DASS(0x78), MS_DELAY(10), \ 123169689Skan MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */), \ 124169689Skan MS_DASS(MS_UNKNOWN /* param */), \ 125169689Skan MS_DELAY(2), \ 126169689Skan MS_CASS(0x0c), MS_DELAY(10), \ 127169689Skan MS_CASS(0x0d), MS_DELAY(2), \ 128169689Skan MS_CASS(0x0c), MS_DELAY(10), \ 129169689Skan MS_DASS(0xff), MS_DELAY(10), \ 130169689Skan MS_RET(0) \ 131169689Skan} 132169689Skan 133169689Skan#define NEGOCIATED_MODE MS_PARAM(2, 1, MS_TYP_CHA) 134169689Skan 135169689Skan#define DECLARE_NEGOCIATE_MICROSEQ \ 136169689Skanstatic struct ppb_microseq negociate_microseq[] = { \ 137169689Skan MS_CASS(0x4), \ 138169689Skan MS_DELAY(5), \ 139169689Skan MS_DASS(MS_UNKNOWN /* mode */), \ 140169689Skan MS_DELAY(100), \ 141169689Skan MS_CASS(0x6), \ 142169689Skan MS_DELAY(5), \ 143169689Skan MS_BRSET(0x20, 5 /* continue */), \ 144169689Skan MS_DELAY(5), \ 145169689Skan MS_CASS(0x7), \ 146169689Skan MS_DELAY(5), \ 147169689Skan MS_CASS(0x6), \ 148169689Skan MS_RET(VP0_ENEGOCIATE), \ 149169689Skan/* continue: */ \ 150169689Skan MS_DELAY(5), \ 151169689Skan MS_CASS(0x7), \ 152169689Skan MS_DELAY(5), \ 153 MS_CASS(0x6), \ 154 MS_RET(0) \ 155} 156 157static struct ppb_microseq reset_microseq[] = { 158 MS_CASS(0x04), 159 MS_DASS(0x40), 160 MS_DELAY(1), 161 MS_CASS(0x0c), 162 MS_CASS(0x0d), 163 MS_DELAY(50), 164 MS_CASS(0x0c), 165 MS_CASS(0x04), 166 MS_RET(0) 167}; 168 169/* 170 * nibble_inbyte_hook() 171 * 172 * Formats high and low nibble into a character 173 */ 174static int 175nibble_inbyte_hook (void *p, char *ptr) 176{ 177 struct vpo_nibble *s = (struct vpo_nibble *)p; 178 179 /* increment the buffer pointer */ 180 *ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0); 181 182 return (0); 183} 184 185/* 186 * Macro used to initialize each vpoio_data structure during 187 * low level attachment 188 * 189 * XXX should be converted to ppb_MS_init_msq() 190 */ 191#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) { \ 192 (vpo)->vpo_nibble_inbyte_msq[6].arg[2].p = \ 193 (void *)&(vpo)->vpo_nibble.h; \ 194 (vpo)->vpo_nibble_inbyte_msq[3].arg[2].p = \ 195 (void *)&(vpo)->vpo_nibble.l; \ 196 (vpo)->vpo_nibble_inbyte_msq[9].arg[0].f = \ 197 nibble_inbyte_hook; \ 198 (vpo)->vpo_nibble_inbyte_msq[9].arg[1].p = \ 199 (void *)&(vpo)->vpo_nibble; \ 200} 201 202/* 203 * This is the sub-microseqence for MS_GET in NIBBLE mode 204 * Retrieve the two nibbles and call the C function to generate the character 205 * and store it in the buffer (see nibble_inbyte_hook()) 206 */ 207static struct ppb_microseq nibble_inbyte_submicroseq[] = { 208 MS_CASS(0x4), 209 210/* loop: */ 211 MS_CASS(0x6), 212 MS_DELAY(1), 213 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */), 214 MS_CASS(0x5), 215 MS_DELAY(1), 216 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */), 217 MS_CASS(0x4), 218 MS_DELAY(1), 219 220 /* do a C call to format the received nibbles */ 221 MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), 222 MS_DBRA(-7 /* loop */), 223 MS_RET(0) 224}; 225 226/* 227 * This is the sub-microseqence for MS_GET in PS2 mode 228 */ 229static struct ppb_microseq ps2_inbyte_submicroseq[] = { 230 MS_CASS(0x4), 231 232/* loop: */ 233 MS_CASS(PCD | 0x6), 234 MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL), 235 MS_CASS(PCD | 0x5), 236 MS_DBRA(-4 /* loop */), 237 238 MS_RET(0) 239}; 240 241/* 242 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes 243 */ 244static struct ppb_microseq spp_outbyte_submicroseq[] = { 245 MS_CASS(0x4), 246 247/* loop: */ 248 MS_RASSERT_P(1, MS_REG_DTR), 249 MS_CASS(0x5), 250 MS_DBRA(0), /* decrement counter */ 251 MS_RASSERT_P(1, MS_REG_DTR), 252 MS_CASS(0x0), 253 MS_DBRA(-6 /* loop */), 254 255 /* return from the put call */ 256 MS_CASS(0x4), 257 MS_RET(0) 258}; 259 260/* EPP 1.7 microsequences, ptr and len set at runtime */ 261static struct ppb_microseq epp17_outstr[] = { 262 MS_CASS(0x4), 263 MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D), 264 MS_CASS(0xc), 265 MS_RET(0), 266}; 267 268static struct ppb_microseq epp17_instr[] = { 269 MS_CASS(PCD | 0x4), 270 MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL), 271 MS_CASS(PCD | 0xc), 272 MS_RET(0), 273}; 274 275static int 276imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus) 277{ 278 DECLARE_CPP_MICROSEQ; 279 280 device_t ppbus = device_get_parent(vpo->vpo_dev); 281 char s1, s2, s3; 282 int ret; 283 284 /* all should be ok */ 285 if (connected) 286 *connected = 0; 287 288 ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1, 289 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3, 290 CPP_PARAM, 0x30); 291 292 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 293 294 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) { 295 if (bootverbose) 296 printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n", 297 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 298 if (connected) 299 *connected = VP0_ECONNECT; 300 } 301 302 if (release_bus) 303 return (ppb_release_bus(ppbus, vpo->vpo_dev)); 304 else 305 return (0); 306} 307 308/* 309 * how : PPB_WAIT or PPB_DONTWAIT 310 */ 311static int 312imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus) 313{ 314 DECLARE_CPP_MICROSEQ; 315 316 device_t ppbus = device_get_parent(vpo->vpo_dev); 317 char s1, s2, s3; 318 int error; 319 int ret; 320 321 /* all should be ok */ 322 if (disconnected) 323 *disconnected = 0; 324 325 if (request_bus) 326 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) 327 return (error); 328 329 ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1, 330 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3); 331 332 /* select device 0 in compatible mode */ 333 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 334 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 335 336 /* disconnect all devices */ 337 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30); 338 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 339 340 if (PPB_IN_EPP_MODE(ppbus)) 341 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28); 342 else 343 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0); 344 345 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret); 346 347 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) { 348 if (bootverbose) 349 printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n", 350 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff); 351 if (disconnected) 352 *disconnected = VP0_ECONNECT; 353 } 354 355 return (0); 356} 357 358/* 359 * imm_detect() 360 * 361 * Detect and initialise the VP0 adapter. 362 */ 363static int 364imm_detect(struct vpoio_data *vpo) 365{ 366 device_t ppbus = device_get_parent(vpo->vpo_dev); 367 int error; 368 369 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT))) 370 return (error); 371 372 /* disconnect the drive, keep the bus */ 373 imm_disconnect(vpo, NULL, 0); 374 375 /* we already have the bus, just connect */ 376 imm_connect(vpo, PPB_DONTWAIT, &error, 0); 377 378 if (error) { 379 if (bootverbose) 380 printf("imm%d: can't connect to the drive\n", 381 vpo->vpo_unit); 382 goto error; 383 } 384 385 /* send SCSI reset signal */ 386 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 387 388 /* release the bus now */ 389 imm_disconnect(vpo, &error, 1); 390 391 /* ensure we are disconnected or daisy chained peripheral 392 * may cause serious problem to the disk */ 393 394 if (error) { 395 if (bootverbose) 396 printf("imm%d: can't disconnect from the drive\n", 397 vpo->vpo_unit); 398 goto error; 399 } 400 401 return (0); 402 403error: 404 ppb_release_bus(ppbus, vpo->vpo_dev); 405 return (VP0_EINITFAILED); 406} 407 408/* 409 * imm_outstr() 410 */ 411static int 412imm_outstr(struct vpoio_data *vpo, char *buffer, int size) 413{ 414 device_t ppbus = device_get_parent(vpo->vpo_dev); 415 int error = 0; 416 417 if (PPB_IN_EPP_MODE(ppbus)) 418 ppb_reset_epp_timeout(ppbus); 419 420 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer, 421 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 422 423 return (error); 424} 425 426/* 427 * imm_instr() 428 */ 429static int 430imm_instr(struct vpoio_data *vpo, char *buffer, int size) 431{ 432 device_t ppbus = device_get_parent(vpo->vpo_dev); 433 int error = 0; 434 435 if (PPB_IN_EPP_MODE(ppbus)) 436 ppb_reset_epp_timeout(ppbus); 437 438 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer, 439 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error); 440 441 return (error); 442} 443 444static char 445imm_select(struct vpoio_data *vpo, int initiator, int target) 446{ 447 DECLARE_SELECT_MICROSEQUENCE; 448 device_t ppbus = device_get_parent(vpo->vpo_dev); 449 int ret; 450 451 /* initialize the select microsequence */ 452 ppb_MS_init_msq(select_microseq, 1, 453 SELECT_TARGET, 1 << initiator | 1 << target); 454 455 ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret); 456 457 return (ret); 458} 459 460/* 461 * imm_wait() 462 * 463 * H_SELIN must be low. 464 * 465 * XXX should be ported to microseq 466 */ 467static char 468imm_wait(struct vpoio_data *vpo, int tmo) 469{ 470 device_t ppbus = device_get_parent(vpo->vpo_dev); 471 register int k; 472 register char r; 473 474 ppb_wctr(ppbus, 0xc); 475 476 /* XXX should be ported to microseq */ 477 k = 0; 478 while (!((r = ppb_rstr(ppbus)) & 0x80) && (k++ < tmo)) 479 DELAY(1); 480 481 /* 482 * Return some status information. 483 * Semantics : 0x88 = ZIP+ wants more data 484 * 0x98 = ZIP+ wants to send more data 485 * 0xa8 = ZIP+ wants command 486 * 0xb8 = end of transfer, ZIP+ is sending status 487 */ 488 ppb_wctr(ppbus, 0x4); 489 if (k < tmo) 490 return (r & 0xb8); 491 492 return (0); /* command timed out */ 493} 494 495static int 496imm_negociate(struct vpoio_data *vpo) 497{ 498 DECLARE_NEGOCIATE_MICROSEQ; 499 device_t ppbus = device_get_parent(vpo->vpo_dev); 500 int negociate_mode; 501 int ret; 502 503 if (PPB_IN_NIBBLE_MODE(ppbus)) 504 negociate_mode = 0; 505 else if (PPB_IN_PS2_MODE(ppbus)) 506 negociate_mode = 1; 507 else 508 return (0); 509 510#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */ 511 ret = ppb_1284_negociate(ppbus, negociate_mode); 512 513 if (ret) 514 return (VP0_ENEGOCIATE); 515#endif 516 517 ppb_MS_init_msq(negociate_microseq, 1, 518 NEGOCIATED_MODE, negociate_mode); 519 520 ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret); 521 522 return (ret); 523} 524 525/* 526 * imm_probe() 527 * 528 * Low level probe of vpo device 529 * 530 */ 531int 532imm_probe(device_t dev, struct vpoio_data *vpo) 533{ 534 int error; 535 536 /* ppbus dependent initialisation */ 537 vpo->vpo_dev = dev; 538 539 /* now, try to initialise the drive */ 540 if ((error = imm_detect(vpo))) { 541 return (error); 542 } 543 544 return (0); 545} 546 547/* 548 * imm_attach() 549 * 550 * Low level attachment of vpo device 551 * 552 */ 553int 554imm_attach(struct vpoio_data *vpo) 555{ 556 device_t ppbus = device_get_parent(vpo->vpo_dev); 557 int epp; 558 559 /* 560 * Initialize microsequence code 561 */ 562 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc( 563 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT); 564 565 if (!vpo->vpo_nibble_inbyte_msq) 566 return (ENXIO); 567 568 bcopy((void *)nibble_inbyte_submicroseq, 569 (void *)vpo->vpo_nibble_inbyte_msq, 570 sizeof(nibble_inbyte_submicroseq)); 571 572 INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo); 573 574 /* 575 * Initialize mode dependent in/out microsequences 576 */ 577 ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT); 578 579 /* enter NIBBLE mode to configure submsq */ 580 if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) { 581 582 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq); 583 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 584 } 585 586 /* enter PS2 mode to configure submsq */ 587 if (ppb_set_mode(ppbus, PPB_PS2) != -1) { 588 589 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq); 590 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq); 591 } 592 593 epp = ppb_get_epp_protocol(ppbus); 594 595 /* enter EPP mode to configure submsq */ 596 if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 597 598 switch (epp) { 599 case EPP_1_9: 600 case EPP_1_7: 601 ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr); 602 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr); 603 break; 604 default: 605 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 606 epp); 607 } 608 } 609 610 /* try to enter EPP or PS/2 mode, NIBBLE otherwise */ 611 if (ppb_set_mode(ppbus, PPB_EPP) != -1) { 612 switch (epp) { 613 case EPP_1_9: 614 printf("imm%d: EPP 1.9 mode\n", vpo->vpo_unit); 615 break; 616 case EPP_1_7: 617 printf("imm%d: EPP 1.7 mode\n", vpo->vpo_unit); 618 break; 619 default: 620 panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__, 621 epp); 622 } 623 } else if (ppb_set_mode(ppbus, PPB_PS2) != -1) 624 printf("imm%d: PS2 mode\n", vpo->vpo_unit); 625 626 else if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) 627 printf("imm%d: NIBBLE mode\n", vpo->vpo_unit); 628 629 else { 630 printf("imm%d: can't enter NIBBLE, PS2 or EPP mode\n", 631 vpo->vpo_unit); 632 633 ppb_release_bus(ppbus, vpo->vpo_dev); 634 635 free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF); 636 return (ENXIO); 637 } 638 639 ppb_release_bus(ppbus, vpo->vpo_dev); 640 641 return (0); 642} 643 644/* 645 * imm_reset_bus() 646 * 647 */ 648int 649imm_reset_bus(struct vpoio_data *vpo) 650{ 651 device_t ppbus = device_get_parent(vpo->vpo_dev); 652 int disconnected; 653 654 /* first, connect to the drive and request the bus */ 655 imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1); 656 657 if (!disconnected) { 658 659 /* reset the SCSI bus */ 660 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL); 661 662 /* then disconnect */ 663 imm_disconnect(vpo, NULL, 1); 664 } 665 666 return (0); 667} 668 669/* 670 * imm_do_scsi() 671 * 672 * Send an SCSI command 673 * 674 */ 675int 676imm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command, 677 int clen, char *buffer, int blen, int *result, int *count, 678 int *ret) 679{ 680 device_t ppbus = device_get_parent(vpo->vpo_dev); 681 register char r; 682 char l, h = 0; 683 int len, error = 0, not_connected = 0; 684 register int k; 685 int negociated = 0; 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 = imm_connect(vpo, PPB_WAIT|PPB_INTR, ¬_connected, 1))) 696 return (error); 697 698 if (not_connected) { 699 *ret = VP0_ECONNECT; goto error; 700 } 701 702 /* 703 * Select the drive ... 704 */ 705 if ((*ret = imm_select(vpo,host,target))) 706 goto error; 707 708 /* 709 * Send the command ... 710 */ 711 for (k = 0; k < clen; k+=2) { 712 if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) { 713 *ret = VP0_ECMD_TIMEOUT; 714 goto error; 715 } 716 if (imm_outstr(vpo, &command[k], 2)) { 717 *ret = VP0_EPPDATA_TIMEOUT; 718 goto error; 719 } 720 } 721 722 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 723 *ret = VP0_ESTATUS_TIMEOUT; goto error; 724 } 725 726 if ((r & 0x30) == 0x10) { 727 if (imm_negociate(vpo)) { 728 *ret = VP0_ENEGOCIATE; 729 goto error; 730 } else 731 negociated = 1; 732 } 733 734 /* 735 * Complete transfer ... 736 */ 737 *count = 0; 738 for (;;) { 739 740 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) { 741 *ret = VP0_ESTATUS_TIMEOUT; goto error; 742 } 743 744 /* stop when the ZIP+ wants to send status */ 745 if (r == (char)0xb8) 746 break; 747 748 if (*count >= blen) { 749 *ret = VP0_EDATA_OVERFLOW; 750 goto error; 751 } 752 753 /* ZIP+ wants to send data? */ 754 if (r == (char)0x88) { 755 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 756 VP0_SECTOR_SIZE : 2; 757 758 error = imm_outstr(vpo, &buffer[*count], len); 759 } else { 760 if (!PPB_IN_EPP_MODE(ppbus)) 761 len = 1; 762 else 763 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ? 764 VP0_SECTOR_SIZE : 1; 765 766 error = imm_instr(vpo, &buffer[*count], len); 767 } 768 769 if (error) { 770 *ret = error; 771 goto error; 772 } 773 774 *count += len; 775 } 776 777 if ((PPB_IN_NIBBLE_MODE(ppbus) || 778 PPB_IN_PS2_MODE(ppbus)) && negociated) 779 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 780 781 /* 782 * Retrieve status ... 783 */ 784 if (imm_negociate(vpo)) { 785 *ret = VP0_ENEGOCIATE; 786 goto error; 787 } else 788 negociated = 1; 789 790 if (imm_instr(vpo, &l, 1)) { 791 *ret = VP0_EOTHER; goto error; 792 } 793 794 /* check if the ZIP+ wants to send more status */ 795 if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8) 796 if (imm_instr(vpo, &h, 1)) { 797 *ret = VP0_EOTHER+2; goto error; 798 } 799 800 *result = ((int) h << 8) | ((int) l & 0xff); 801 802error: 803 if ((PPB_IN_NIBBLE_MODE(ppbus) || 804 PPB_IN_PS2_MODE(ppbus)) && negociated) 805 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL); 806 807 /* return to printer state, release the ppbus */ 808 imm_disconnect(vpo, NULL, 1); 809 810 return (0); 811} 812