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