vpo.c revision 28257
1/*-
2 * Copyright (c) 1997 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: vpo.c,v 1.1 1997/08/14 13:57:44 msmith Exp $
27 *
28 */
29#include <sys/types.h>
30
31#ifdef KERNEL
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/errno.h>
35#include <sys/malloc.h>
36#include <sys/buf.h>
37#include <sys/proc.h>
38#include <sys/syslog.h>
39
40#include <machine/stdarg.h>
41#include <machine/clock.h>
42
43#include <i386/isa/isa_device.h>
44#endif	/* KERNEL */
45#include <scsi/scsi_all.h>
46#include <scsi/scsi_disk.h>
47#include <scsi/scsiconf.h>
48
49#ifdef	KERNEL
50#include <sys/kernel.h>
51#endif /*KERNEL */
52
53#include <dev/ppbus/ppbconf.h>
54#include <dev/ppbus/vpo.h>
55
56/* --------------------------------------------------------------------
57 * HERE ARE THINGS YOU MAY HAVE/WANT TO CHANGE
58 */
59
60/*
61 * XXX
62 * We may add a timeout queue to avoid active polling on nACK.
63 */
64#define VP0_SELTMO		5000	/* select timeout */
65#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
66#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
67
68/*
69 * DO NOT MODIFY ANYTHING UNDER THIS LINE
70 * --------------------------------------------------------------------
71 */
72
73static inline int vpoio_do_scsi(struct vpo_data *, int, int, char *, int,
74				char *, int, int *, int *);
75
76static int32_t	vpo_scsi_cmd(struct scsi_xfer *);
77static void	vpominphys(struct buf *);
78static u_int32_t vpo_adapter_info(int);
79
80static int	vpo_detect(struct vpo_data *vpo);
81
82static int	nvpo = 0;
83#define MAXVP0	8			/* XXX not much better! */
84static struct vpo_data *vpodata[MAXVP0];
85
86#ifdef KERNEL
87static struct scsi_adapter vpo_switch =
88{
89	vpo_scsi_cmd,
90	vpominphys,
91	0,
92	0,
93	vpo_adapter_info,
94	"vpo",
95	{ 0, 0 }
96};
97
98/*
99 * The below structure is so we have a default dev struct
100 * for out link struct.
101 */
102static struct scsi_device vpo_dev =
103{
104	NULL,	/* Use default error handler */
105	NULL,	/* have a queue, served by this */
106	NULL,	/* have no async handler */
107	NULL,	/* Use default 'done' routine */
108	"vpo",
109	0,
110	{ 0, 0 }
111};
112
113
114/*
115 * Make ourselves visible as a ppbus driver
116 */
117
118static struct ppb_device	*vpoprobe(struct ppb_data *ppb);
119static int			vpoattach(struct ppb_device *dev);
120
121static struct ppb_driver vpodriver = {
122    vpoprobe, vpoattach, "vpo"
123};
124DATA_SET(ppbdriver_set, vpodriver);
125
126
127#endif /* KERNEL */
128
129static u_int32_t
130vpo_adapter_info(int unit)
131{
132
133	return 1;
134}
135
136/*
137 * vpoprobe()
138 *
139 * Called by ppb_attachdevs().
140 */
141static struct ppb_device *
142vpoprobe(struct ppb_data *ppb)
143{
144
145	struct vpo_data *vpo;
146
147	if (nvpo >= MAXVP0) {
148		printf("vpo: Too many devices (max %d)\n", MAXVP0);
149		return(NULL);
150	}
151
152	vpo = (struct vpo_data *)malloc(sizeof(struct vpo_data),
153							M_DEVBUF, M_NOWAIT);
154	if (!vpo) {
155		printf("vpo: cannot malloc!\n");
156		return(NULL);
157	}
158	bzero(vpo, sizeof(struct vpo_data));
159
160	vpodata[nvpo] = vpo;
161
162	/* vpo dependent initialisation */
163	vpo->vpo_unit = nvpo;
164
165	/* ppbus dependent initialisation */
166	vpo->vpo_dev.id_unit = vpo->vpo_unit;
167	vpo->vpo_dev.ppb = ppb;
168
169	/* now, try to initialise the drive */
170	if (vpo_detect(vpo)) {
171		free(vpo, M_DEVBUF);
172		return (NULL);
173	}
174
175	/* ok, go to next device on next probe */
176	nvpo ++;
177
178	return (&vpo->vpo_dev);
179}
180
181/*
182 * vpoattach()
183 *
184 * Called by ppb_attachdevs().
185 */
186static int
187vpoattach(struct ppb_device *dev)
188{
189
190	struct scsibus_data *scbus;
191	struct vpo_data *vpo = vpodata[dev->id_unit];
192
193	vpo->sc_link.adapter_unit = vpo->vpo_unit;
194	vpo->sc_link.adapter_targ = VP0_INITIATOR;
195	vpo->sc_link.adapter = &vpo_switch;
196	vpo->sc_link.device = &vpo_dev;
197	vpo->sc_link.opennings = VP0_OPENNINGS;
198
199	/*
200	 * Report ourselves
201	 */
202	printf("vpo%d: <Adaptec aic7110 scsi> on ppbus %d\n",
203	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
204
205	/*
206	 * Prepare the scsibus_data area for the upperlevel
207	 * scsi code.
208	 */
209	scbus = scsi_alloc_bus();
210	if(!scbus)
211		return (0);
212	scbus->adapter_link = &vpo->sc_link;
213
214	scsi_attachdevs(scbus);
215
216	return (1);
217}
218
219static void
220vpominphys(struct buf *bp)
221{
222
223	if (bp->b_bcount > VP0_BUFFER_SIZE)
224		bp->b_bcount = VP0_BUFFER_SIZE;
225
226	return;
227}
228
229#ifdef VP0_WARNING
230static inline void
231vpo_warning(struct vpo_data *vpo, struct scsi_xfer *xs, int timeout)
232{
233
234	switch (timeout) {
235	case 0:
236	case VP0_ESELECT_TIMEOUT:
237		/* log(LOG_WARNING,
238			"vpo%d: select timeout\n", vpo->vpo_unit); */
239		break;
240	case VP0_EDISCONNECT:
241		log(LOG_WARNING,
242			"vpo%d: can't get printer state\n", vpo->vpo_unit);
243		break;
244	case VP0_ECONNECT:
245		log(LOG_WARNING,
246			"vpo%d: can't get disk state\n", vpo->vpo_unit);
247		break;
248	case VP0_ECMD_TIMEOUT:
249		log(LOG_WARNING,
250			"vpo%d: command timeout\n", vpo->vpo_unit);
251		break;
252	case VP0_EPPDATA_TIMEOUT:
253		log(LOG_WARNING,
254			"vpo%d: EPP data timeout\n", vpo->vpo_unit);
255		break;
256	case VP0_ESTATUS_TIMEOUT:
257		log(LOG_WARNING,
258			"vpo%d: status timeout\n", vpo->vpo_unit);
259		break;
260	case VP0_EDATA_OVERFLOW:
261		log(LOG_WARNING,
262			"vpo%d: data overflow\n", vpo->vpo_unit);
263		break;
264	case VP0_EINTR:
265		log(LOG_WARNING,
266			"vpo%d: ppb request interrupted\n", vpo->vpo_unit);
267		break;
268	default:
269		log(LOG_WARNING,
270			"vpo%d: timeout = %d\n", vpo->vpo_unit, timeout);
271		break;
272	}
273}
274#endif /* VP0_WARNING */
275
276/*
277 * vpointr()
278 */
279static inline void
280vpointr(struct vpo_data *vpo, struct scsi_xfer *xs)
281{
282
283	int errno;	/* error in errno.h */
284
285	if (xs->datalen && !(xs->flags & SCSI_DATA_IN))
286		bcopy(xs->data, vpo->vpo_buffer, xs->datalen);
287
288	errno = vpoio_do_scsi(vpo, VP0_INITIATOR,
289		xs->sc_link->target, (char *)xs->cmd, xs->cmdlen,
290		vpo->vpo_buffer, xs->datalen, &vpo->vpo_stat, &vpo->vpo_count);
291
292#ifdef VP0_DEBUG
293	printf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n",
294		 errno, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error);
295#endif
296
297	if (errno) {
298#ifdef VP0_WARNING
299		log(LOG_WARNING, "vpo%d: errno = %d\n", vpo->vpo_unit, errno);
300#endif
301		/* connection to ppbus interrupted */
302		xs->error = XS_DRIVER_STUFFUP;
303		goto error;
304	}
305
306	/* if a timeout occured, no sense */
307	if (vpo->vpo_error) {
308#ifdef VP0_WARNING
309		vpo_warning(vpo, xs, vpo->vpo_error);
310#endif
311		xs->error = XS_TIMEOUT;
312		goto error;
313	}
314
315#define RESERVED_BITS_MASK 0x3e		/* 00111110b */
316#define NO_SENSE	0x0
317#define CHECK_CONDITION	0x02
318
319	switch (vpo->vpo_stat & RESERVED_BITS_MASK) {
320	case NO_SENSE:
321		break;
322
323	case CHECK_CONDITION:
324		vpo->vpo_sense.cmd.op_code = REQUEST_SENSE;
325		vpo->vpo_sense.cmd.length = sizeof(xs->sense);
326		vpo->vpo_sense.cmd.control = 0;
327
328		errno = vpoio_do_scsi(vpo, VP0_INITIATOR,
329			xs->sc_link->target, (char *)&vpo->vpo_sense.cmd,
330			sizeof(vpo->vpo_sense.cmd),
331			(char *)&xs->sense, sizeof(xs->sense),
332			&vpo->vpo_sense.stat, &vpo->vpo_sense.count);
333
334		if (errno)
335			/* connection to ppbus interrupted */
336			xs->error = XS_DRIVER_STUFFUP;
337		else
338			xs->error = XS_SENSE;
339
340		goto error;
341
342	default:	/* BUSY or RESERVATION_CONFLICT */
343		xs->error = XS_TIMEOUT;
344		goto error;
345	}
346
347	if (xs->datalen && (xs->flags & SCSI_DATA_IN))
348		bcopy(vpo->vpo_buffer, xs->data, xs->datalen);
349
350done:
351	xs->resid = 0;
352	xs->error = XS_NOERROR;
353
354error:
355	xs->flags |= ITSDONE;
356	scsi_done(xs);
357
358	return;
359}
360
361static int32_t
362vpo_scsi_cmd(struct scsi_xfer *xs)
363{
364
365	int s;
366
367	if (xs->sc_link->lun > 0) {
368		xs->error = XS_DRIVER_STUFFUP;
369		return TRY_AGAIN_LATER;
370	}
371
372	if (xs->flags & SCSI_DATA_UIO) {
373		printf("UIO not supported by vpo_driver !\n");
374		xs->error = XS_DRIVER_STUFFUP;
375		return TRY_AGAIN_LATER;
376	}
377
378#ifdef VP0_DEBUG
379	printf("vpo_scsi_cmd(): xs->flags = 0x%x, "\
380		"xs->data = 0x%x, xs->datalen = %d\ncommand : %*D\n",
381		xs->flags, xs->data, xs->datalen,
382		xs->cmdlen, xs->cmd, " " );
383#endif
384
385	if (xs->flags & SCSI_NOMASK) {
386		vpointr(vpodata[xs->sc_link->adapter_unit], xs);
387		return COMPLETE;
388	}
389
390	s = VP0_SPL();
391
392	vpointr(vpodata[xs->sc_link->adapter_unit], xs);
393
394	splx(s);
395	return SUCCESSFULLY_QUEUED;
396}
397
398#define vpoio_d_pulse(vpo,b) { \
399	ppb_wdtr(&(vpo)->vpo_dev, b); \
400	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
401	ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
402	ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
403	ppb_wctr(&(vpo)->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
404	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
405	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO |  H_SELIN | H_INIT | H_STROBE); \
406	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO |  H_SELIN | H_INIT | H_STROBE); \
407	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO |  H_SELIN | H_INIT | H_STROBE); \
408	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
409	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
410	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
411}
412
413#define vpoio_c_pulse(vpo,b) { \
414	ppb_wdtr(&(vpo)->vpo_dev, b); \
415	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
416	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO |  H_SELIN | H_INIT | H_STROBE); \
417	ppb_wctr(&(vpo)->vpo_dev, H_nAUTO |  H_SELIN | H_INIT | H_STROBE); \
418	ppb_wctr(&(vpo)->vpo_dev, H_nAUTO |  H_SELIN | H_INIT | H_STROBE); \
419	ppb_wctr(&(vpo)->vpo_dev, H_nAUTO |  H_SELIN | H_INIT | H_STROBE); \
420	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO |  H_SELIN | H_INIT | H_STROBE); \
421	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
422	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
423	ppb_wctr(&(vpo)->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE); \
424}
425
426static int
427vpoio_disconnect(struct vpo_data *vpo)
428{
429
430	vpoio_d_pulse(vpo, 0);
431	vpoio_d_pulse(vpo, 0x3c);
432	vpoio_d_pulse(vpo, 0x20);
433	vpoio_d_pulse(vpo, 0xf);
434
435	return (ppb_release_bus(&vpo->vpo_dev));
436}
437
438/*
439 * how	: PPB_WAIT or PPB_DONTWAIT
440 */
441static int
442vpoio_connect(struct vpo_data *vpo, int how)
443{
444	int error;
445
446	if ((error = ppb_request_bus(&vpo->vpo_dev, how)))
447		return error;
448
449	vpoio_c_pulse(vpo, 0);
450	vpoio_c_pulse(vpo, 0x3c);
451	vpoio_c_pulse(vpo, 0x20);
452
453	if (PPB_IN_EPP_MODE(&vpo->vpo_dev)) {
454		vpoio_c_pulse(vpo, 0xcf);
455	} else {
456		vpoio_c_pulse(vpo, 0x8f);
457	}
458
459	return (0);
460}
461
462/*
463 * vpoio_in_disk_mode()
464 *
465 * Check if we are in disk mode
466 */
467static int
468vpoio_in_disk_mode(struct vpo_data *vpo)
469{
470
471	/* first, set H_AUTO high */
472	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
473
474	/* when H_AUTO is set low, H_FLT should be high */
475	ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE);
476	if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) == 0)
477		return (0);
478
479	/* when H_AUTO is set high, H_FLT should be low */
480	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
481	if ((ppb_rstr(&vpo->vpo_dev) & H_FLT) != 0)
482		return (0);
483
484	return (1);
485}
486
487/*
488 * vpoio_reset()
489 *
490 * SCSI reset signal, the drive must be in disk mode
491 */
492static void
493vpoio_reset (struct vpo_data *vpo)
494{
495
496	/*
497	 * SCSI reset signal.
498	 */
499	ppb_wdtr(&vpo->vpo_dev, (1 << 7));
500	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_nINIT | H_STROBE);
501	DELAY(25);
502	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN |  H_INIT | H_STROBE);
503
504	return;
505}
506
507
508/*
509 * vpo_detect()
510 *
511 * Detect and initialise the VP0 adapter.
512 */
513static int
514vpo_detect(struct vpo_data *vpo)
515{
516
517	vpoio_disconnect(vpo);
518	vpoio_connect(vpo, PPB_DONTWAIT);
519
520	if (!vpoio_in_disk_mode(vpo)) {
521		vpoio_disconnect(vpo);
522		return (VP0_EINITFAILED);
523	}
524
525	/* send SCSI reset signal */
526	vpoio_reset (vpo);
527
528	vpoio_disconnect(vpo);
529
530	if (vpoio_in_disk_mode(vpo))
531		return (VP0_EINITFAILED);
532
533	return (0);
534}
535
536#define vpo_wctr(dev,byte,delay) {			 \
537	int i; int iter = delay / MHZ_16_IO_DURATION;	 \
538	for (i = 0; i < iter; i++) {			 \
539		ppb_wctr(dev, byte);			 \
540	}						 \
541}
542
543#define vpoio_spp_outbyte(vpo,byte) {					 \
544	ppb_wdtr(&vpo->vpo_dev, byte);					 \
545	ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN | H_INIT | H_STROBE); \
546	vpo_wctr(&vpo->vpo_dev,  H_AUTO | H_nSELIN | H_INIT | H_STROBE,	 \
547		VP0_SPP_WRITE_PULSE);					 \
548}
549
550#define vpoio_nibble_inbyte(vpo,buffer) {				\
551	register char h, l;						\
552	vpo_wctr(&vpo->vpo_dev,  H_AUTO | H_SELIN | H_INIT | H_STROBE,	\
553		VP0_NIBBLE_READ_PULSE);					\
554	h = ppb_rstr(&vpo->vpo_dev);					\
555	ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_SELIN | H_INIT | H_STROBE);	\
556	l = ppb_rstr(&vpo->vpo_dev);					\
557	*buffer = ((l >> 4) & 0x0f) + (h & 0xf0);			\
558}
559
560#define vpoio_ps2_inbyte(vpo,buffer) {					\
561	*buffer = ppb_rdtr(&vpo->vpo_dev);				\
562	ppb_wctr(&vpo->vpo_dev, PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE); \
563	ppb_wctr(&vpo->vpo_dev, PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE); \
564}
565
566/*
567 * vpoio_outstr()
568 */
569static int
570vpoio_outstr(struct vpo_data *vpo, char *buffer, int size)
571{
572
573	register int k;
574	int error = 0;
575	int r, mode, epp;
576
577	mode = ppb_get_mode(&vpo->vpo_dev);
578	switch (mode) {
579		case PPB_NIBBLE:
580		case PPB_PS2:
581			for (k = 0; k < size; k++) {
582				vpoio_spp_outbyte(vpo, *buffer++);
583			}
584			break;
585
586		case PPB_EPP:
587		case PPB_ECP_EPP:
588			epp = ppb_get_epp_protocol(&vpo->vpo_dev);
589
590			ppb_reset_epp_timeout(&vpo->vpo_dev);
591			ppb_wctr(&vpo->vpo_dev,
592				H_AUTO | H_SELIN | H_INIT | H_STROBE);
593
594			if (epp == EPP_1_7)
595				for (k = 0; k < size; k++) {
596					ppb_wepp(&vpo->vpo_dev, *buffer++);
597					if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
598						error = VP0_EPPDATA_TIMEOUT;
599						break;
600					}
601				}
602			else {
603				if (((long) buffer | size) & 0x03)
604					ppb_outsb_epp(&vpo->vpo_dev,
605							buffer, size);
606				else
607					ppb_outsl_epp(&vpo->vpo_dev,
608							buffer, size/4);
609
610				if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
611					error = VP0_EPPDATA_TIMEOUT;
612					break;
613				}
614			}
615			ppb_wctr(&vpo->vpo_dev,
616				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
617			/* ppb_ecp_sync(&vpo->vpo_dev); */
618			break;
619
620		default:
621			printf("vpoio_outstr(): unknown transfer mode (%d)!\n",
622				mode);
623			return (1);		/* XXX */
624	}
625
626	return (error);
627}
628
629/*
630 * vpoio_instr()
631 */
632static int
633vpoio_instr(struct vpo_data *vpo, char *buffer, int size)
634{
635
636	register int k;
637	int error = 0;
638	int r, mode, epp;
639
640	mode = ppb_get_mode(&vpo->vpo_dev);
641	switch (mode) {
642		case PPB_NIBBLE:
643			for (k = 0; k < size; k++) {
644				vpoio_nibble_inbyte(vpo, buffer++);
645			}
646			ppb_wctr(&vpo->vpo_dev,
647				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
648			break;
649
650		case PPB_PS2:
651			ppb_wctr(&vpo->vpo_dev, PCD |
652				H_AUTO | H_SELIN | H_INIT | H_nSTROBE);
653
654			for (k = 0; k < size; k++) {
655				vpoio_ps2_inbyte(vpo, buffer++);
656			}
657			ppb_wctr(&vpo->vpo_dev,
658				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
659			break;
660
661		case PPB_EPP:
662		case PPB_ECP_EPP:
663			epp = ppb_get_epp_protocol(&vpo->vpo_dev);
664
665			ppb_reset_epp_timeout(&vpo->vpo_dev);
666			ppb_wctr(&vpo->vpo_dev, PCD |
667				H_AUTO | H_SELIN | H_INIT | H_STROBE);
668
669			if (epp == EPP_1_7)
670				for (k = 0; k < size; k++) {
671					*buffer++ = ppb_repp(&vpo->vpo_dev);
672					if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
673						error = VP0_EPPDATA_TIMEOUT;
674						break;
675					}
676				}
677			else {
678				if (((long) buffer | size) & 0x03)
679					ppb_insb_epp(&vpo->vpo_dev,
680							buffer, size);
681				else
682					ppb_insl_epp(&vpo->vpo_dev,
683							buffer, size/4);
684
685				if ((ppb_rstr(&vpo->vpo_dev) & TIMEOUT)) {
686					error = VP0_EPPDATA_TIMEOUT;
687					break;
688				}
689			}
690			ppb_wctr(&vpo->vpo_dev, PCD |
691				H_AUTO | H_nSELIN | H_INIT | H_STROBE);
692			/* ppb_ecp_sync(&vpo->vpo_dev); */
693			break;
694
695		default:
696			printf("vpoio_instr(): unknown transfer mode (%d)!\n",
697				mode);
698			return (1);		/* XXX */
699	}
700
701	return (error);
702}
703
704static inline char
705vpoio_select(struct vpo_data *vpo, int initiator, int target)
706{
707
708	register int	k;
709
710	ppb_wdtr(&vpo->vpo_dev, (1 << target));
711	ppb_wctr(&vpo->vpo_dev, H_nAUTO | H_nSELIN |  H_INIT | H_STROBE);
712	ppb_wctr(&vpo->vpo_dev,  H_AUTO | H_nSELIN |  H_INIT | H_STROBE);
713	ppb_wdtr(&vpo->vpo_dev, (1 << initiator));
714	ppb_wctr(&vpo->vpo_dev,  H_AUTO | H_nSELIN | H_nINIT | H_STROBE);
715
716	k = 0;
717	while (!(ppb_rstr(&vpo->vpo_dev) & 0x40) && (k++ < VP0_SELTMO))
718		barrier();
719
720	if (k >= VP0_SELTMO)
721		return (VP0_ESELECT_TIMEOUT);
722
723	return (0);
724}
725
726/*
727 * vpoio_wait()
728 *
729 * H_SELIN must be low.
730 */
731static inline char
732vpoio_wait(struct vpo_data *vpo, int tmo)
733{
734
735	register int	k;
736	register char	r;
737
738#if 0	/* broken */
739	if (ppb_poll_device(&vpo->vpo_dev, 150, nBUSY, nBUSY, PPB_INTR))
740		return (0);
741
742	return (ppb_rstr(&vpo->vpo_dev) & 0xf0);
743#endif
744
745	k = 0;
746	while (!((r = ppb_rstr(&vpo->vpo_dev)) & nBUSY) && (k++ < tmo))
747		barrier();
748
749	/*
750	 * Return some status information.
751	 * Semantics :	0xc0 = ZIP wants more data
752	 *		0xd0 = ZIP wants to send more data
753	 *		0xe0 = ZIP wants command
754	 *		0xf0 = end of transfer, ZIP is sending status
755	 */
756	if (k < tmo)
757	  return (r & 0xf0);
758
759	return (0);			   /* command timed out */
760}
761
762static inline int
763vpoio_do_scsi(struct vpo_data *vpo, int host, int target, char *command,
764		int clen, char *buffer, int blen, int *result, int *count)
765{
766
767	register char r;
768	char l, h = 0;
769	int rw, len, error = 0;
770	register int k;
771
772	/*
773	 * enter disk state, allocate the ppbus
774	 *
775	 * XXX
776	 * Should we allow this call to be interruptible?
777	 * The only way to report the interruption is to return
778	 * EIO do upper SCSI code :^(
779	 */
780	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
781		return (error);
782
783	if (!vpoio_in_disk_mode(vpo)) {
784		vpo->vpo_error = VP0_ECONNECT; goto error;
785	}
786
787	if ((vpo->vpo_error = vpoio_select(vpo,host,target)))
788		goto error;
789
790	/*
791	 * Send the command ...
792	 *
793	 * set H_SELIN low for vpoio_wait().
794	 */
795	ppb_wctr(&vpo->vpo_dev, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
796
797#ifdef VP0_DEBUG
798	printf("vpo%d: drive selected, now sending the command...\n",
799		vpo->vpo_unit);
800#endif
801
802	for (k = 0; k < clen; k++) {
803		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
804			vpo->vpo_error = VP0_ECMD_TIMEOUT;
805			goto error;
806		}
807		if (vpoio_outstr(vpo, &command[k], 1)) {
808			vpo->vpo_error = VP0_EPPDATA_TIMEOUT;
809			goto error;
810		}
811	}
812
813#ifdef VP0_DEBUG
814	printf("vpo%d: command sent, now completing the request...\n",
815		vpo->vpo_unit);
816#endif
817
818	/*
819	 * Completion ...
820	 */
821	rw = ((command[0] == READ_COMMAND) || (command[0] == READ_BIG) ||
822		(command[0] == WRITE_COMMAND) || (command[0] == WRITE_BIG));
823
824	*count = 0;
825	for (;;) {
826
827		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
828			vpo->vpo_error = VP0_ESTATUS_TIMEOUT; goto error;
829		}
830
831		/* stop when the ZIP wants to send status */
832		if (r == (char)0xf0)
833			break;
834
835		if (*count >= blen) {
836			vpo->vpo_error = VP0_EDATA_OVERFLOW;
837			goto error;
838		}
839		len = (rw && ((blen - *count) >= VP0_SECTOR_SIZE)) ?
840			VP0_SECTOR_SIZE : 1;
841
842		/* ZIP wants to send data? */
843		if (r == (char)0xc0)
844			error = vpoio_outstr(vpo, &buffer[*count], len);
845		else
846			error = vpoio_instr(vpo, &buffer[*count], len);
847
848		if (error) {
849			vpo->vpo_error = error;
850			goto error;
851		}
852
853		*count += len;
854	}
855
856	if (vpoio_instr(vpo, &l, 1)) {
857		vpo->vpo_error = VP0_EOTHER; goto error;
858	}
859
860	/* check if the ZIP wants to send more status */
861	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
862		if (vpoio_instr(vpo, &h, 1)) {
863			vpo->vpo_error = VP0_EOTHER+2; goto error;
864		}
865
866	*result = ((int) h << 8) | ((int) l & 0xff);
867
868error:
869	/* return to printer state, release the ppbus */
870	vpoio_disconnect(vpo);
871	return (0);
872}
873