vpoio.c revision 39134
1/*-
2 * Copyright (c) 1998 Nicolas Souchu
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id: vpoio.c,v 1.1.2.6 1998/08/07 01:59:49 son Exp $
27 *
28 */
29
30#ifdef KERNEL
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/malloc.h>
34#include <sys/buf.h>
35
36#include <machine/clock.h>
37
38#endif	/* KERNEL */
39
40#ifdef	KERNEL
41#include <sys/kernel.h>
42#endif /*KERNEL */
43
44#include <dev/ppbus/ppbconf.h>
45#include <dev/ppbus/ppb_msq.h>
46#include <dev/ppbus/vpoio.h>
47
48/*
49 * The driver pools the drive. We may add a timeout queue to avoid
50 * active polling on nACK. I've tried this but it leads to unreliable
51 * transfers
52 */
53#define VP0_SELTMO		5000	/* select timeout */
54#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
55#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
56
57/*
58 * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
59 * but succeeding in respecting such timings leads to architecture
60 * dependent considerations.
61 */
62#define VP0_PULSE		1
63
64#define VP0_SECTOR_SIZE	512
65#define VP0_BUFFER_SIZE	0x12000
66
67#define n(flags) (~(flags) & (flags))
68
69/*
70 * VP0 connections.
71 */
72#define H_AUTO		n(AUTOFEED)
73#define H_nAUTO		AUTOFEED
74#define H_STROBE	n(STROBE)
75#define H_nSTROBE	STROBE
76#define H_BSY		n(nBUSY)
77#define H_nBSY		nBUSY
78#define H_SEL		SELECT
79#define H_nSEL		n(SELECT)
80#define H_ERR		PERROR
81#define H_nERR		n(PERROR)
82#define H_ACK		nACK
83#define H_nACK		n(nACK)
84#define H_FLT		nFAULT
85#define H_nFLT		n(nFAULT)
86#define H_SELIN		n(SELECTIN)
87#define H_nSELIN	SELECTIN
88#define H_INIT		nINIT
89#define H_nINIT		n(nINIT)
90
91/*
92 * Microcode to execute very fast I/O sequences at the lowest bus level.
93 */
94
95/* call this macro to initialize connect/disconnect microsequences */
96#define INIT_TRIG_MICROSEQ {						\
97	int i;								\
98	for (i=1; i <= 7; i+=2) {					\
99		disconnect_microseq[i].arg[2] = (void *)d_pulse;	\
100		connect_epp_microseq[i].arg[2] = 			\
101		connect_spp_microseq[i].arg[2] = (void *)c_pulse;	\
102	}								\
103}
104
105#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
106static char d_pulse[] = {
107	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
108	H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
109	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
110	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
111	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
112};
113
114#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */)
115static char c_pulse[] = {
116	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
117	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
118	H_nAUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
119	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
120	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
121};
122
123static struct ppb_microseq disconnect_microseq[] = {
124	  MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
125	  MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
126};
127
128static struct ppb_microseq connect_epp_microseq[] = {
129	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
130	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
131};
132
133static struct ppb_microseq connect_spp_microseq[] = {
134	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
135	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
136};
137
138/*
139 * nibble_inbyte_hook()
140 *
141 * Formats high and low nibble into a character
142 */
143static int
144nibble_inbyte_hook (void *p, char *ptr)
145{
146	struct vpo_nibble *s = (struct vpo_nibble *)p;
147
148	/* increment the buffer pointer */
149	*ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
150
151	return (0);
152}
153
154/*
155 * Macro used to initialize each vpoio_data structure during
156 * low level attachment
157 *
158 * XXX should be converted to ppb_MS_init_msq()
159 */
160#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
161	(vpo)->vpo_nibble_inbyte_msq[2].arg[2].p =		\
162			(void *)&(vpo)->vpo_nibble.h;		\
163	(vpo)->vpo_nibble_inbyte_msq[4].arg[2].p =		\
164			(void *)&(vpo)->vpo_nibble.l;		\
165	(vpo)->vpo_nibble_inbyte_msq[5].arg[0].f =		\
166			nibble_inbyte_hook;			\
167	(vpo)->vpo_nibble_inbyte_msq[5].arg[1].p =		\
168			(void *)&(vpo)->vpo_nibble;		\
169}
170
171/*
172 * This is the sub-microseqence for MS_GET in NIBBLE mode
173 * Retrieve the two nibbles and call the C function to generate the character
174 * and store it in the buffer (see nibble_inbyte_hook())
175 */
176static struct ppb_microseq nibble_inbyte_submicroseq[] = {
177
178/* loop: */
179	  MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE),
180	  MS_DELAY(VP0_PULSE),
181	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
182	  MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE),
183	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
184
185	  /* do a C call to format the received nibbles */
186	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
187	  MS_DBRA(-6 /* loop */),
188
189	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
190	  MS_RET(0)
191};
192
193/*
194 * This is the sub-microseqence for MS_GET in PS2 mode
195 */
196static struct ppb_microseq ps2_inbyte_submicroseq[] = {
197	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
198
199/* loop: */
200	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
201	  MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
202	  MS_CASS(PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
203	  MS_DBRA(-3 /* loop */),
204
205	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
206	  MS_RET(0)
207};
208
209/*
210 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
211 */
212static struct ppb_microseq spp_outbyte_submicroseq[] = {
213
214/* loop: */
215	  MS_RASSERT_P(1, MS_REG_DTR),
216	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
217	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
218	  MS_DELAY(VP0_PULSE),
219	  MS_DBRA(-4 /* loop */),
220
221	  /* return from the put call */
222	  MS_RET(0)
223};
224
225/* EPP 1.7 microsequences, ptr and len set at runtime */
226static struct ppb_microseq epp17_outstr_body[] = {
227	  MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
228
229/* loop: */
230	  MS_RASSERT_P(1, MS_REG_EPP),
231	  MS_BRSET(TIMEOUT, 4 /* error */),	/* EPP timeout? */
232	  MS_DBRA(-2 /* loop */),
233
234	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
235	  MS_RET(0),
236/* error: */
237	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
238	  MS_RET(1)
239};
240
241static struct ppb_microseq epp17_instr_body[] = {
242	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE),
243
244/* loop: */
245	  MS_RFETCH_P(1, MS_REG_EPP, MS_FETCH_ALL),
246	  MS_BRSET(TIMEOUT, 4 /* error */),	/* EPP timeout? */
247	  MS_DBRA(-2 /* loop */),
248
249	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
250	  MS_RET(0),
251/* error: */
252	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
253	  MS_RET(1)
254};
255
256static struct ppb_microseq in_disk_mode[] = {
257	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
258	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
259
260	  MS_BRCLEAR(H_FLT, 4 /* error */),
261	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
262	  MS_BRSET(H_FLT, 2 /* error */),
263
264	  MS_RET(0),
265/* error: */
266	  MS_RET(1)
267};
268
269static int
270vpoio_disconnect(struct vpoio_data *vpo)
271{
272	int ret;
273
274	ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret);
275	return (ppb_release_bus(&vpo->vpo_dev));
276}
277
278/*
279 * how	: PPB_WAIT or PPB_DONTWAIT
280 */
281static int
282vpoio_connect(struct vpoio_data *vpo, int how)
283{
284	int error;
285	int ret;
286
287	if ((error = ppb_request_bus(&vpo->vpo_dev, how)))
288		return error;
289
290	if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
291		ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret);
292	else
293		ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret);
294
295	return (0);
296}
297
298/*
299 * vpoio_reset()
300 *
301 * SCSI reset signal, the drive must be in disk mode
302 */
303static void
304vpoio_reset (struct vpoio_data *vpo)
305{
306	int ret;
307
308	struct ppb_microseq reset_microseq[] = {
309
310		#define INITIATOR	MS_PARAM(0, 1, MS_TYP_INT)
311
312		MS_DASS(MS_UNKNOWN),
313		MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
314		MS_DELAY(25),
315		MS_CASS(H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
316		MS_RET(0)
317	};
318
319	ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR);
320	ppb_MS_microseq(&vpo->vpo_dev, reset_microseq, &ret);
321
322	return;
323}
324
325/*
326 * vpoio_in_disk_mode()
327 */
328static int
329vpoio_in_disk_mode(struct vpoio_data *vpo)
330{
331	int ret;
332
333	ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
334
335	return (ret);
336}
337
338/*
339 * vpoio_detect()
340 *
341 * Detect and initialise the VP0 adapter.
342 */
343static int
344vpoio_detect(struct vpoio_data *vpo)
345{
346	vpoio_disconnect(vpo);
347	vpoio_connect(vpo, PPB_DONTWAIT);
348
349	if (vpoio_in_disk_mode(vpo)) {
350		vpoio_disconnect(vpo);
351		return (VP0_EINITFAILED);
352	}
353
354	/* send SCSI reset signal */
355	vpoio_reset(vpo);
356
357	vpoio_disconnect(vpo);
358
359	/* ensure we are disconnected or daisy chained peripheral
360	 * may cause serious problem to the disk */
361
362	if (!vpoio_in_disk_mode(vpo))
363		return (VP0_EINITFAILED);
364
365	return (0);
366}
367
368/*
369 * vpoio_outstr()
370 */
371static int
372vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
373{
374
375	int error = 0;
376
377	ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error);
378
379#if 0
380		/* XXX EPP 1.9 not implemented with microsequences */
381		else {
382
383			ppb_reset_epp_timeout(&vpo->vpo_dev);
384			ppb_wctr(&vpo->vpo_dev,
385				H_AUTO | H_SELIN | H_INIT | H_STROBE);
386
387			if (((long) buffer | size) & 0x03)
388				ppb_outsb_epp(&vpo->vpo_dev,
389						buffer, size);
390			else
391				ppb_outsl_epp(&vpo->vpo_dev,
392						buffer, size/4);
393
394			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
395				error = VP0_EPPDATA_TIMEOUT;
396				goto error;
397			}
398
399			ppb_wctr(&vpo->vpo_dev,
400				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
401		}
402		/* ppb_ecp_sync(&vpo->vpo_dev); */
403#endif
404
405	return (error);
406}
407
408/*
409 * vpoio_instr()
410 */
411static int
412vpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
413{
414
415	register int k;
416	int error = 0;
417	int r, mode, epp;
418
419	ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error);
420
421#if 0
422		/* XXX EPP 1.9 not implemented with microsequences */
423		else {
424
425			ppb_reset_epp_timeout(&vpo->vpo_dev);
426			ppb_wctr(&vpo->vpo_dev, PCD |
427				H_AUTO | H_SELIN | H_INIT | H_STROBE);
428
429			if (((long) buffer | size) & 0x03)
430				ppb_insb_epp(&vpo->vpo_dev,
431						buffer, size);
432			else
433				ppb_insl_epp(&vpo->vpo_dev,
434						buffer, size/4);
435
436			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
437				error = VP0_EPPDATA_TIMEOUT;
438				goto error;
439			}
440
441			ppb_wctr(&vpo->vpo_dev, PCD |
442				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
443		}
444		/* ppb_ecp_sync(&vpo->vpo_dev); */
445#endif
446
447	return (error);
448}
449
450static char
451vpoio_select(struct vpoio_data *vpo, int initiator, int target)
452{
453	register int	k;
454	int ret;
455
456	struct ppb_microseq select_microseq[] = {
457
458		/* parameter list
459		 */
460		#define SELECT_TARGET		MS_PARAM(0, 1, MS_TYP_INT)
461		#define SELECT_INITIATOR	MS_PARAM(3, 1, MS_TYP_INT)
462
463		/* send the select command to the drive */
464		MS_DASS(MS_UNKNOWN),
465		MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
466		MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
467		MS_DASS(MS_UNKNOWN),
468		MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
469
470		/* now, wait until the drive is ready */
471		MS_SET(VP0_SELTMO),
472/* loop: */	MS_BRSET(H_ACK, 3 /* ready */),
473		MS_DBRA(-1 /* loop */),
474/* error: */	MS_RET(1),
475/* ready: */	MS_RET(0)
476	};
477
478	/* initialize the select microsequence */
479	ppb_MS_init_msq(select_microseq, 2,
480			SELECT_TARGET, 1 << target,
481			SELECT_INITIATOR, 1 << initiator);
482
483	ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret);
484
485	if (ret)
486		return (VP0_ESELECT_TIMEOUT);
487
488	return (0);
489}
490
491/*
492 * vpoio_wait()
493 *
494 * H_SELIN must be low.
495 *
496 * XXX should be ported to microseq
497 */
498static char
499vpoio_wait(struct vpoio_data *vpo, int tmo)
500{
501
502	register int	k;
503	register char	r;
504
505#if 0	/* broken */
506	if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR))
507		return (0);
508
509	return (ppb_rstr(&vpo->vpo_dev) & 0xf0);
510#endif
511
512	/* XXX should be ported to microseq */
513	k = 0;
514	while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo))
515		;
516
517	/*
518	 * Return some status information.
519	 * Semantics :	0xc0 = ZIP wants more data
520	 *		0xd0 = ZIP wants to send more data
521	 *		0xe0 = ZIP wants command
522	 *		0xf0 = end of transfer, ZIP is sending status
523	 */
524	if (k < tmo)
525	  return (r & 0xf0);
526
527	return (0);			   /* command timed out */
528}
529
530/*
531 * vpoio_probe()
532 *
533 * Low level probe of vpo device
534 *
535 */
536struct ppb_device *
537vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo)
538{
539
540	/* ppbus dependent initialisation */
541	vpo->vpo_dev.id_unit = vpo->vpo_unit;
542	vpo->vpo_dev.name = "vpo";
543	vpo->vpo_dev.ppb = ppb;
544
545	/*
546	 * Initialize microsequence code
547	 */
548	INIT_TRIG_MICROSEQ;
549
550	/* now, try to initialise the drive */
551	if (vpoio_detect(vpo)) {
552		return (NULL);
553	}
554
555	return (&vpo->vpo_dev);
556}
557
558/*
559 * vpoio_attach()
560 *
561 * Low level attachment of vpo device
562 *
563 */
564int
565vpoio_attach(struct vpoio_data *vpo)
566{
567	int epp;
568
569	/*
570	 * Report ourselves
571	 */
572	printf("vpo%d: <Iomega VPI0 Parallel to SCSI interface> on ppbus %d\n",
573		vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit);
574
575	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
576		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
577
578	if (!vpo->vpo_nibble_inbyte_msq)
579		return (0);
580
581	bcopy((void *)nibble_inbyte_submicroseq,
582		(void *)vpo->vpo_nibble_inbyte_msq,
583		sizeof(nibble_inbyte_submicroseq));
584
585	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
586
587	/*
588	 * Initialize mode dependent in/out microsequences
589	 */
590	ppb_request_bus(&vpo->vpo_dev, PPB_WAIT);
591
592	/* enter NIBBLE mode to configure submsq */
593	if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) {
594
595		ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
596
597		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
598	}
599
600	/* enter PS2 mode to configure submsq */
601	if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) {
602
603		ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq);
604
605		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
606	}
607
608	epp = ppb_get_epp_protocol(&vpo->vpo_dev);
609
610	/* enter EPP mode to configure submsq */
611	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
612
613		switch (epp) {
614		case EPP_1_9:
615			/* XXX EPP 1.9 support should be improved */
616		case EPP_1_7:
617			ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body);
618
619			ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body);
620			break;
621		default:
622			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
623				epp);
624		}
625	}
626
627	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
628	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
629		switch (epp) {
630		case EPP_1_9:
631			printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit);
632			break;
633		case EPP_1_7:
634			printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit);
635			break;
636		default:
637			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
638				epp);
639		}
640	} else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1)
641		printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
642
643	else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1)
644		printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
645
646	else {
647		printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n",
648			vpo->vpo_unit);
649
650		ppb_release_bus(&vpo->vpo_dev);
651
652		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
653		return (0);
654	}
655
656	ppb_release_bus(&vpo->vpo_dev);
657
658	return (1);
659}
660
661/*
662 * vpoio_reset_bus()
663 *
664 */
665int
666vpoio_reset_bus(struct vpoio_data *vpo)
667{
668	/* first, connect to the drive */
669	if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || vpoio_in_disk_mode(vpo)) {
670		/* release ppbus */
671		vpoio_disconnect(vpo);
672		return (1);
673	}
674
675	/* reset the SCSI bus */
676	vpoio_reset(vpo);
677
678	/* then disconnect */
679	vpoio_disconnect(vpo);
680
681	return (0);
682}
683
684/*
685 * vpoio_do_scsi()
686 *
687 * Send an SCSI command
688 *
689 */
690int
691vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
692		int clen, char *buffer, int blen, int *result, int *count,
693		int *ret)
694{
695
696	register char r;
697	char l, h = 0;
698	int len, error = 0;
699	register int k;
700
701	/*
702	 * enter disk state, allocate the ppbus
703	 *
704	 * XXX
705	 * Should we allow this call to be interruptible?
706	 * The only way to report the interruption is to return
707	 * EIO do upper SCSI code :^(
708	 */
709	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
710		return (error);
711
712	if (vpoio_in_disk_mode(vpo)) {
713		*ret = VP0_ECONNECT; goto error;
714	}
715
716	if ((*ret = vpoio_select(vpo,host,target)))
717		goto error;
718
719	/*
720	 * Send the command ...
721	 *
722	 * set H_SELIN low for vpoio_wait().
723	 */
724	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
725
726	for (k = 0; k < clen; k++) {
727		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
728			*ret = VP0_ECMD_TIMEOUT;
729			goto error;
730		}
731		if (vpoio_outstr(vpo, &command[k], 1)) {
732			*ret = VP0_EPPDATA_TIMEOUT;
733			goto error;
734		}
735	}
736
737	/*
738	 * Completion ...
739	 */
740
741	*count = 0;
742	for (;;) {
743
744		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
745			*ret = VP0_ESTATUS_TIMEOUT; goto error;
746		}
747
748		/* stop when the ZIP wants to send status */
749		if (r == (char)0xf0)
750			break;
751
752		if (*count >= blen) {
753			*ret = VP0_EDATA_OVERFLOW;
754			goto error;
755		}
756		len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
757			VP0_SECTOR_SIZE : 1;
758
759		/* ZIP wants to send data? */
760		if (r == (char)0xc0)
761			error = vpoio_outstr(vpo, &buffer[*count], len);
762		else
763			error = vpoio_instr(vpo, &buffer[*count], len);
764
765		if (error) {
766			*ret = error;
767			goto error;
768		}
769
770		*count += len;
771	}
772
773	if (vpoio_instr(vpo, &l, 1)) {
774		*ret = VP0_EOTHER; goto error;
775	}
776
777	/* check if the ZIP wants to send more status */
778	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
779		if (vpoio_instr(vpo, &h, 1)) {
780			*ret = VP0_EOTHER+2; goto error;
781		}
782
783	*result = ((int) h << 8) | ((int) l & 0xff);
784
785error:
786	/* return to printer state, release the ppbus */
787	vpoio_disconnect(vpo);
788	return (0);
789}
790