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