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