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