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