vpoio.c revision 39520
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.2 1998/09/13 18:26:26 nsouch 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(1),
265/* error: */
266	  MS_RET(0)
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	int error, ret;
347
348	/* allocate the bus, then apply microsequences */
349	if ((error = ppb_request_bus(&vpo->vpo_dev, PPB_DONTWAIT)))
350                return (error);
351
352	ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret);
353
354	if (PPB_IN_EPP_MODE(&vpo->vpo_dev))
355		ppb_MS_microseq(&vpo->vpo_dev, connect_epp_microseq, &ret);
356	else
357		ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret);
358
359	ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
360	if (!ret) {
361
362		/* try spp mode (maybe twice or because previous mode was PS2)
363		 * NIBBLE mode will be restored on next transfers if detection
364		 * succeed
365		 */
366		ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE);
367		ppb_MS_microseq(&vpo->vpo_dev, connect_spp_microseq, &ret);
368
369		ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
370		if (!ret) {
371			if (bootverbose)
372				printf("vpo%d: can't connect to the drive\n",
373					vpo->vpo_unit);
374
375			/* disconnect and release the bus */
376			ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq,
377					&ret);
378			goto error;
379		}
380	}
381
382	/* send SCSI reset signal */
383	vpoio_reset(vpo);
384
385	ppb_MS_microseq(&vpo->vpo_dev, disconnect_microseq, &ret);
386
387	/* ensure we are disconnected or daisy chained peripheral
388	 * may cause serious problem to the disk */
389
390	ppb_MS_microseq(&vpo->vpo_dev, in_disk_mode, &ret);
391	if (ret) {
392		if (bootverbose)
393			printf("vpo%d: can't disconnect from the drive\n",
394				vpo->vpo_unit);
395		goto error;
396	}
397
398	ppb_release_bus(&vpo->vpo_dev);
399	return (0);
400
401error:
402	ppb_release_bus(&vpo->vpo_dev);
403	return (VP0_EINITFAILED);
404}
405
406/*
407 * vpoio_outstr()
408 */
409static int
410vpoio_outstr(struct vpoio_data *vpo, char *buffer, int size)
411{
412
413	int error = 0;
414
415	ppb_MS_exec(&vpo->vpo_dev, MS_OP_PUT, buffer, size, MS_UNKNOWN, &error);
416
417#if 0
418		/* XXX EPP 1.9 not implemented with microsequences */
419		else {
420
421			ppb_reset_epp_timeout(&vpo->vpo_dev);
422			ppb_wctr(&vpo->vpo_dev,
423				H_AUTO | H_SELIN | H_INIT | H_STROBE);
424
425			if (((long) buffer | size) & 0x03)
426				ppb_outsb_epp(&vpo->vpo_dev,
427						buffer, size);
428			else
429				ppb_outsl_epp(&vpo->vpo_dev,
430						buffer, size/4);
431
432			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
433				error = VP0_EPPDATA_TIMEOUT;
434				goto error;
435			}
436
437			ppb_wctr(&vpo->vpo_dev,
438				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
439		}
440		/* ppb_ecp_sync(&vpo->vpo_dev); */
441#endif
442
443	return (error);
444}
445
446/*
447 * vpoio_instr()
448 */
449static int
450vpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
451{
452
453	register int k;
454	int error = 0;
455	int r, mode, epp;
456
457	ppb_MS_exec(&vpo->vpo_dev, MS_OP_GET, buffer, size, MS_UNKNOWN, &error);
458
459#if 0
460		/* XXX EPP 1.9 not implemented with microsequences */
461		else {
462
463			ppb_reset_epp_timeout(&vpo->vpo_dev);
464			ppb_wctr(&vpo->vpo_dev, PCD |
465				H_AUTO | H_SELIN | H_INIT | H_STROBE);
466
467			if (((long) buffer | size) & 0x03)
468				ppb_insb_epp(&vpo->vpo_dev,
469						buffer, size);
470			else
471				ppb_insl_epp(&vpo->vpo_dev,
472						buffer, size/4);
473
474			if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
475				error = VP0_EPPDATA_TIMEOUT;
476				goto error;
477			}
478
479			ppb_wctr(&vpo->vpo_dev, PCD |
480				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
481		}
482		/* ppb_ecp_sync(&vpo->vpo_dev); */
483#endif
484
485	return (error);
486}
487
488static char
489vpoio_select(struct vpoio_data *vpo, int initiator, int target)
490{
491	register int	k;
492	int ret;
493
494	struct ppb_microseq select_microseq[] = {
495
496		/* parameter list
497		 */
498		#define SELECT_TARGET		MS_PARAM(0, 1, MS_TYP_INT)
499		#define SELECT_INITIATOR	MS_PARAM(3, 1, MS_TYP_INT)
500
501		/* send the select command to the drive */
502		MS_DASS(MS_UNKNOWN),
503		MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
504		MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
505		MS_DASS(MS_UNKNOWN),
506		MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
507
508		/* now, wait until the drive is ready */
509		MS_SET(VP0_SELTMO),
510/* loop: */	MS_BRSET(H_ACK, 3 /* ready */),
511		MS_DBRA(-1 /* loop */),
512/* error: */	MS_RET(1),
513/* ready: */	MS_RET(0)
514	};
515
516	/* initialize the select microsequence */
517	ppb_MS_init_msq(select_microseq, 2,
518			SELECT_TARGET, 1 << target,
519			SELECT_INITIATOR, 1 << initiator);
520
521	ppb_MS_microseq(&vpo->vpo_dev, select_microseq, &ret);
522
523	if (ret)
524		return (VP0_ESELECT_TIMEOUT);
525
526	return (0);
527}
528
529/*
530 * vpoio_wait()
531 *
532 * H_SELIN must be low.
533 *
534 * XXX should be ported to microseq
535 */
536static char
537vpoio_wait(struct vpoio_data *vpo, int tmo)
538{
539
540	register int	k;
541	register char	r;
542
543#if 0	/* broken */
544	if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR))
545		return (0);
546
547	return (ppb_rstr(&vpo->vpo_dev) & 0xf0);
548#endif
549
550	/* XXX should be ported to microseq */
551	k = 0;
552	while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo))
553		;
554
555	/*
556	 * Return some status information.
557	 * Semantics :	0xc0 = ZIP wants more data
558	 *		0xd0 = ZIP wants to send more data
559	 *		0xe0 = ZIP wants command
560	 *		0xf0 = end of transfer, ZIP is sending status
561	 */
562	if (k < tmo)
563	  return (r & 0xf0);
564
565	return (0);			   /* command timed out */
566}
567
568/*
569 * vpoio_probe()
570 *
571 * Low level probe of vpo device
572 *
573 */
574struct ppb_device *
575vpoio_probe(struct ppb_data *ppb, struct vpoio_data *vpo)
576{
577
578	/* ppbus dependent initialisation */
579	vpo->vpo_dev.id_unit = vpo->vpo_unit;
580	vpo->vpo_dev.name = "vpo";
581	vpo->vpo_dev.ppb = ppb;
582
583	/*
584	 * Initialize microsequence code
585	 */
586	INIT_TRIG_MICROSEQ;
587
588	/* now, try to initialise the drive */
589	if (vpoio_detect(vpo)) {
590		return (NULL);
591	}
592
593	return (&vpo->vpo_dev);
594}
595
596/*
597 * vpoio_attach()
598 *
599 * Low level attachment of vpo device
600 *
601 */
602int
603vpoio_attach(struct vpoio_data *vpo)
604{
605	int epp;
606
607	/*
608	 * Report ourselves
609	 */
610	printf("vpo%d: <Iomega VPI0 Parallel to SCSI interface> on ppbus %d\n",
611		vpo->vpo_dev.id_unit, vpo->vpo_dev.ppb->ppb_link->adapter_unit);
612
613	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
614		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
615
616	if (!vpo->vpo_nibble_inbyte_msq)
617		return (0);
618
619	bcopy((void *)nibble_inbyte_submicroseq,
620		(void *)vpo->vpo_nibble_inbyte_msq,
621		sizeof(nibble_inbyte_submicroseq));
622
623	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
624
625	/*
626	 * Initialize mode dependent in/out microsequences
627	 */
628	ppb_request_bus(&vpo->vpo_dev, PPB_WAIT);
629
630	/* enter NIBBLE mode to configure submsq */
631	if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1) {
632
633		ppb_MS_GET_init(&vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
634
635		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
636	}
637
638	/* enter PS2 mode to configure submsq */
639	if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1) {
640
641		ppb_MS_GET_init(&vpo->vpo_dev, ps2_inbyte_submicroseq);
642
643		ppb_MS_PUT_init(&vpo->vpo_dev, spp_outbyte_submicroseq);
644	}
645
646	epp = ppb_get_epp_protocol(&vpo->vpo_dev);
647
648	/* enter EPP mode to configure submsq */
649	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
650
651		switch (epp) {
652		case EPP_1_9:
653			/* XXX EPP 1.9 support should be improved */
654		case EPP_1_7:
655			ppb_MS_GET_init(&vpo->vpo_dev, epp17_instr_body);
656
657			ppb_MS_PUT_init(&vpo->vpo_dev, epp17_outstr_body);
658			break;
659		default:
660			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
661				epp);
662		}
663	}
664
665	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
666	if (ppb_set_mode(&vpo->vpo_dev, PPB_EPP) != -1) {
667		switch (epp) {
668		case EPP_1_9:
669			printf("vpo%d: EPP 1.9 mode\n", vpo->vpo_unit);
670			break;
671		case EPP_1_7:
672			printf("vpo%d: EPP 1.7 mode\n", vpo->vpo_unit);
673			break;
674		default:
675			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
676				epp);
677		}
678	} else if (ppb_set_mode(&vpo->vpo_dev, PPB_PS2) != -1)
679		printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
680
681	else if (ppb_set_mode(&vpo->vpo_dev, PPB_NIBBLE) != -1)
682		printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
683
684	else {
685		printf("vpo%d: can't enter NIBBLE, PS2 or EPP mode\n",
686			vpo->vpo_unit);
687
688		ppb_release_bus(&vpo->vpo_dev);
689
690		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
691		return (0);
692	}
693
694	ppb_release_bus(&vpo->vpo_dev);
695
696	return (1);
697}
698
699/*
700 * vpoio_reset_bus()
701 *
702 */
703int
704vpoio_reset_bus(struct vpoio_data *vpo)
705{
706	/* first, connect to the drive */
707	if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
708		/* release ppbus */
709		vpoio_disconnect(vpo);
710		return (1);
711	}
712
713	/* reset the SCSI bus */
714	vpoio_reset(vpo);
715
716	/* then disconnect */
717	vpoio_disconnect(vpo);
718
719	return (0);
720}
721
722/*
723 * vpoio_do_scsi()
724 *
725 * Send an SCSI command
726 *
727 */
728int
729vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
730		int clen, char *buffer, int blen, int *result, int *count,
731		int *ret)
732{
733
734	register char r;
735	char l, h = 0;
736	int len, error = 0;
737	register int k;
738
739	/*
740	 * enter disk state, allocate the ppbus
741	 *
742	 * XXX
743	 * Should we allow this call to be interruptible?
744	 * The only way to report the interruption is to return
745	 * EIO do upper SCSI code :^(
746	 */
747	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
748		return (error);
749
750	if (!vpoio_in_disk_mode(vpo)) {
751		*ret = VP0_ECONNECT; goto error;
752	}
753
754	if ((*ret = vpoio_select(vpo,host,target)))
755		goto error;
756
757	/*
758	 * Send the command ...
759	 *
760	 * set H_SELIN low for vpoio_wait().
761	 */
762	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
763
764	for (k = 0; k < clen; k++) {
765		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
766			*ret = VP0_ECMD_TIMEOUT;
767			goto error;
768		}
769		if (vpoio_outstr(vpo, &command[k], 1)) {
770			*ret = VP0_EPPDATA_TIMEOUT;
771			goto error;
772		}
773	}
774
775	/*
776	 * Completion ...
777	 */
778
779	*count = 0;
780	for (;;) {
781
782		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
783			*ret = VP0_ESTATUS_TIMEOUT; goto error;
784		}
785
786		/* stop when the ZIP wants to send status */
787		if (r == (char)0xf0)
788			break;
789
790		if (*count >= blen) {
791			*ret = VP0_EDATA_OVERFLOW;
792			goto error;
793		}
794
795		/* if in EPP mode or writing bytes, try to transfer a sector
796		 * otherwise, just send one byte
797		 */
798		if (PPB_IN_EPP_MODE(&vpo->vpo_dev) || r == (char)0xc0)
799			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
800				VP0_SECTOR_SIZE : 1;
801		else
802			len = 1;
803
804		/* ZIP wants to send data? */
805		if (r == (char)0xc0)
806			error = vpoio_outstr(vpo, &buffer[*count], len);
807		else
808			error = vpoio_instr(vpo, &buffer[*count], len);
809
810		if (error) {
811			*ret = error;
812			goto error;
813		}
814
815		*count += len;
816	}
817
818	if (vpoio_instr(vpo, &l, 1)) {
819		*ret = VP0_EOTHER; goto error;
820	}
821
822	/* check if the ZIP wants to send more status */
823	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
824		if (vpoio_instr(vpo, &h, 1)) {
825			*ret = VP0_EOTHER+2; goto error;
826		}
827
828	*result = ((int) h << 8) | ((int) l & 0xff);
829
830error:
831	/* return to printer state, release the ppbus */
832	vpoio_disconnect(vpo);
833	return (0);
834}
835