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