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