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