vpoio.c revision 39134
1156952Sume/*-
2156952Sume * Copyright (c) 1998 Nicolas Souchu
3156952Sume * All rights reserved.
4156952Sume *
5156952Sume * Redistribution and use in source and binary forms, with or without
6156952Sume * modification, are permitted provided that the following conditions
7156952Sume * are met:
8156952Sume * 1. Redistributions of source code must retain the above copyright
9156952Sume *    notice, this list of conditions and the following disclaimer.
10156952Sume * 2. Redistributions in binary form must reproduce the above copyright
11156952Sume *    notice, this list of conditions and the following disclaimer in the
12156952Sume *    documentation and/or other materials provided with the distribution.
13156952Sume *
14156952Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15156952Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16156952Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17156952Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18156952Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19156952Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20156952Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21186090Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22156952Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23156952Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24156952Sume * SUCH DAMAGE.
25156952Sume *
26156952Sume *	$Id: vpoio.c,v 1.1.2.6 1998/08/07 01:59:49 son Exp $
27156952Sume *
28156952Sume */
29156952Sume
30156952Sume#ifdef KERNEL
31156952Sume#include <sys/param.h>
32186090Sume#include <sys/systm.h>
33186090Sume#include <sys/malloc.h>
34156952Sume#include <sys/buf.h>
35156952Sume
36156952Sume#include <machine/clock.h>
37156952Sume
38156952Sume#endif	/* KERNEL */
39156952Sume
40156952Sume#ifdef	KERNEL
41156952Sume#include <sys/kernel.h>
42156952Sume#endif /*KERNEL */
43156952Sume
44156952Sume#include <dev/ppbus/ppbconf.h>
45156952Sume#include <dev/ppbus/ppb_msq.h>
46156952Sume#include <dev/ppbus/vpoio.h>
47156952Sume
48156952Sume/*
49156952Sume * The driver pools the drive. We may add a timeout queue to avoid
50156952Sume * active polling on nACK. I've tried this but it leads to unreliable
51156952Sume * transfers
52156952Sume */
53156952Sume#define VP0_SELTMO		5000	/* select timeout */
54156952Sume#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
55156952Sume#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
56156952Sume
57156952Sume/*
58156952Sume * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
59156952Sume * but succeeding in respecting such timings leads to architecture
60156952Sume * dependent considerations.
61156952Sume */
62156952Sume#define VP0_PULSE		1
63156952Sume
64156952Sume#define VP0_SECTOR_SIZE	512
65156952Sume#define VP0_BUFFER_SIZE	0x12000
66156952Sume
67156952Sume#define n(flags) (~(flags) & (flags))
68156952Sume
69156952Sume/*
70156952Sume * VP0 connections.
71156952Sume */
72156952Sume#define H_AUTO		n(AUTOFEED)
73156952Sume#define H_nAUTO		AUTOFEED
74156952Sume#define H_STROBE	n(STROBE)
75156952Sume#define H_nSTROBE	STROBE
76156952Sume#define H_BSY		n(nBUSY)
77156952Sume#define H_nBSY		nBUSY
78156952Sume#define H_SEL		SELECT
79156952Sume#define H_nSEL		n(SELECT)
80156952Sume#define H_ERR		PERROR
81156952Sume#define H_nERR		n(PERROR)
82156952Sume#define H_ACK		nACK
83156952Sume#define H_nACK		n(nACK)
84156952Sume#define H_FLT		nFAULT
85156952Sume#define H_nFLT		n(nFAULT)
86156952Sume#define H_SELIN		n(SELECTIN)
87156952Sume#define H_nSELIN	SELECTIN
88156952Sume#define H_INIT		nINIT
89156952Sume#define H_nINIT		n(nINIT)
90156952Sume
91156952Sume/*
92156952Sume * Microcode to execute very fast I/O sequences at the lowest bus level.
93156952Sume */
94156952Sume
95156952Sume/* call this macro to initialize connect/disconnect microsequences */
96156952Sume#define INIT_TRIG_MICROSEQ {						\
97156952Sume	int i;								\
98156952Sume	for (i=1; i <= 7; i+=2) {					\
99156952Sume		disconnect_microseq[i].arg[2] = (void *)d_pulse;	\
100156952Sume		connect_epp_microseq[i].arg[2] = 			\
101156952Sume		connect_spp_microseq[i].arg[2] = (void *)c_pulse;	\
102156952Sume	}								\
103156952Sume}
104156952Sume
105156952Sume#define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
106156952Sumestatic char d_pulse[] = {
107156952Sume	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
108156952Sume	H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
109156952Sume	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
110156952Sume	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
111156952Sume	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
112156952Sume};
113156952Sume
114156952Sume#define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */)
115156952Sumestatic char c_pulse[] = {
116156952Sume	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
117156952Sume	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
118156952Sume	H_nAUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
119156952Sume	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
120156952Sume	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
121156952Sume};
122156952Sume
123156952Sumestatic struct ppb_microseq disconnect_microseq[] = {
124156952Sume	  MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
125156952Sume	  MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
126156952Sume};
127156952Sume
128156952Sumestatic struct ppb_microseq connect_epp_microseq[] = {
129156952Sume	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
130156952Sume	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
131156952Sume};
132156952Sume
133156952Sumestatic struct ppb_microseq connect_spp_microseq[] = {
134156952Sume	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
135156952Sume	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
136156952Sume};
137156952Sume
138156952Sume/*
139156952Sume * nibble_inbyte_hook()
140156952Sume *
141156952Sume * Formats high and low nibble into a character
142156952Sume */
143156952Sumestatic int
144156952Sumenibble_inbyte_hook (void *p, char *ptr)
145156952Sume{
146156952Sume	struct vpo_nibble *s = (struct vpo_nibble *)p;
147156952Sume
148156952Sume	/* increment the buffer pointer */
149156952Sume	*ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
150156952Sume
151156952Sume	return (0);
152156952Sume}
153156952Sume
154156952Sume/*
155156952Sume * Macro used to initialize each vpoio_data structure during
156156952Sume * low level attachment
157156952Sume *
158156952Sume * XXX should be converted to ppb_MS_init_msq()
159156952Sume */
160156952Sume#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
161156952Sume	(vpo)->vpo_nibble_inbyte_msq[2].arg[2].p =		\
162156952Sume			(void *)&(vpo)->vpo_nibble.h;		\
163156952Sume	(vpo)->vpo_nibble_inbyte_msq[4].arg[2].p =		\
164156952Sume			(void *)&(vpo)->vpo_nibble.l;		\
165156952Sume	(vpo)->vpo_nibble_inbyte_msq[5].arg[0].f =		\
166156952Sume			nibble_inbyte_hook;			\
167156952Sume	(vpo)->vpo_nibble_inbyte_msq[5].arg[1].p =		\
168156952Sume			(void *)&(vpo)->vpo_nibble;		\
169156952Sume}
170156952Sume
171156952Sume/*
172156952Sume * This is the sub-microseqence for MS_GET in NIBBLE mode
173156952Sume * Retrieve the two nibbles and call the C function to generate the character
174156952Sume * and store it in the buffer (see nibble_inbyte_hook())
175156952Sume */
176156952Sumestatic struct ppb_microseq nibble_inbyte_submicroseq[] = {
177156952Sume
178156952Sume/* loop: */
179156952Sume	  MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE),
180156952Sume	  MS_DELAY(VP0_PULSE),
181156952Sume	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
182156952Sume	  MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE),
183156952Sume	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
184156952Sume
185156952Sume	  /* do a C call to format the received nibbles */
186156952Sume	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
187156952Sume	  MS_DBRA(-6 /* loop */),
188156952Sume
189156952Sume	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
190156952Sume	  MS_RET(0)
191156952Sume};
192156952Sume
193156952Sume/*
194156952Sume * This is the sub-microseqence for MS_GET in PS2 mode
195156952Sume */
196156952Sumestatic struct ppb_microseq ps2_inbyte_submicroseq[] = {
197156952Sume	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
198156952Sume
199156952Sume/* loop: */
200156952Sume	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
201156952Sume	  MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
202156952Sume	  MS_CASS(PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
203156952Sume	  MS_DBRA(-3 /* loop */),
204156952Sume
205170242Sume	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
206170242Sume	  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