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