ppb_msq.c revision 54903
1/*-
2 * Copyright (c) 1998 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/ppb_msq.c 54903 1999-12-20 17:50:24Z eivind $
27 *
28 */
29#include <machine/stdarg.h>
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34
35#include <dev/ppbus/ppbconf.h>
36#include <dev/ppbus/ppb_msq.h>
37
38/* msq index (see PPB_MAX_XFER)
39 * These are device modes
40 */
41#define COMPAT_MSQ	0x0
42#define NIBBLE_MSQ	0x1
43#define PS2_MSQ		0x2
44#define EPP17_MSQ	0x3
45#define EPP19_MSQ	0x4
46#define ECP_MSQ		0x5
47
48/*
49 * Device mode to submsq conversion
50 */
51static struct ppb_xfer *
52mode2xfer(struct ppb_device *dev, int opcode)
53{
54	int index, epp;
55	struct ppb_xfer *table;
56
57	switch (opcode) {
58	case MS_OP_GET:
59		table = dev->get_xfer;
60		break;
61
62	case MS_OP_PUT:
63		table = dev->put_xfer;
64		break;
65
66	default:
67		panic("%s: unknown opcode (%d)", __FUNCTION__, opcode);
68	}
69
70	/* retrieve the device operating mode */
71	switch (ppb_get_mode(dev)) {
72	case PPB_COMPATIBLE:
73		index = COMPAT_MSQ;
74		break;
75	case PPB_NIBBLE:
76		index = NIBBLE_MSQ;
77		break;
78	case PPB_PS2:
79		index = PS2_MSQ;
80		break;
81	case PPB_EPP:
82		switch ((epp = ppb_get_epp_protocol(dev))) {
83		case EPP_1_7:
84			index = EPP17_MSQ;
85			break;
86		case EPP_1_9:
87			index = EPP19_MSQ;
88			break;
89		default:
90			panic("%s: unknown EPP protocol (0x%x)!", __FUNCTION__,
91				epp);
92		}
93		break;
94	case PPB_ECP:
95		index = ECP_MSQ;
96		break;
97	default:
98		panic("%s: unknown mode (%d)", __FUNCTION__, dev->mode);
99	}
100
101	return (&table[index]);
102}
103
104/*
105 * ppb_MS_init()
106 *
107 * Initialize device dependent submicrosequence of the current mode
108 *
109 */
110int
111ppb_MS_init(struct ppb_device *dev, struct ppb_microseq *loop, int opcode)
112{
113	struct ppb_xfer *xfer = mode2xfer(dev, opcode);
114
115	xfer->loop = loop;
116
117	return (0);
118}
119
120/*
121 * ppb_MS_exec()
122 *
123 * Execute any microsequence opcode - expensive
124 *
125 */
126int
127ppb_MS_exec(struct ppb_device *dev, int opcode, union ppb_insarg param1,
128		union ppb_insarg param2, union ppb_insarg param3, int *ret)
129{
130	struct ppb_microseq msq[] = {
131		  { MS_UNKNOWN, { { MS_UNKNOWN }, { MS_UNKNOWN }, { MS_UNKNOWN } } },
132		  MS_RET(0)
133	};
134
135	/* initialize the corresponding microseq */
136	msq[0].opcode = opcode;
137	msq[0].arg[0] = param1;
138	msq[0].arg[1] = param2;
139	msq[0].arg[2] = param3;
140
141	/* execute the microseq */
142	return (ppb_MS_microseq(dev, msq, ret));
143}
144
145/*
146 * ppb_MS_loop()
147 *
148 * Execute a microseq loop
149 *
150 */
151int
152ppb_MS_loop(struct ppb_device *dev, struct ppb_microseq *prolog,
153		struct ppb_microseq *body, struct ppb_microseq *epilog,
154		int iter, int *ret)
155{
156	struct ppb_microseq loop_microseq[] = {
157		  MS_CALL(0),			/* execute prolog */
158
159		  MS_SET(MS_UNKNOWN),		/* set size of transfer */
160	/* loop: */
161		  MS_CALL(0),			/* execute body */
162		  MS_DBRA(-1 /* loop: */),
163
164		  MS_CALL(0),			/* execute epilog */
165		  MS_RET(0)
166	};
167
168	/* initialize the structure */
169	loop_microseq[0].arg[0].p = (void *)prolog;
170	loop_microseq[1].arg[0].i = iter;
171	loop_microseq[2].arg[0].p = (void *)body;
172	loop_microseq[4].arg[0].p = (void *)epilog;
173
174	/* execute the loop */
175	return (ppb_MS_microseq(dev, loop_microseq, ret));
176}
177
178/*
179 * ppb_MS_init_msq()
180 *
181 * Initialize a microsequence - see macros in ppb_msq.h
182 *
183 */
184int
185ppb_MS_init_msq(struct ppb_microseq *msq, int nbparam, ...)
186{
187	int i;
188	int param, ins, arg, type;
189	va_list p_list = 0;
190
191	va_start(p_list, nbparam);
192
193	for (i=0; i<nbparam; i++) {
194		/* retrieve the parameter descriptor */
195		param = va_arg(p_list, int);
196
197		ins  = MS_INS(param);
198		arg  = MS_ARG(param);
199		type = MS_TYP(param);
200
201		/* check the instruction position */
202		if (arg >= PPB_MS_MAXARGS)
203			panic("%s: parameter out of range (0x%x)!",
204				__FUNCTION__, param);
205
206#if 0
207		printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
208			__FUNCTION__, param, ins, arg, type);
209#endif
210
211		/* properly cast the parameter */
212		switch (type) {
213		case MS_TYP_INT:
214			msq[ins].arg[arg].i = va_arg(p_list, int);
215			break;
216
217		case MS_TYP_CHA:
218			msq[ins].arg[arg].i = (int)va_arg(p_list, char);
219			break;
220
221		case MS_TYP_PTR:
222			msq[ins].arg[arg].p = va_arg(p_list, void *);
223			break;
224
225		case MS_TYP_FUN:
226			msq[ins].arg[arg].f = va_arg(p_list, void *);
227			break;
228
229		default:
230			panic("%s: unknown parameter (0x%x)!", __FUNCTION__,
231				param);
232		}
233	}
234
235	return (0);
236}
237
238/*
239 * ppb_MS_microseq()
240 *
241 * Interprete a microsequence. Some microinstructions are executed at adapter
242 * level to avoid function call overhead between ppbus and the adapter
243 */
244int
245ppb_MS_microseq(struct ppb_device *dev, struct ppb_microseq *msq, int *ret)
246{
247	struct ppb_data *ppb = dev->ppb;
248	struct ppb_microseq *mi;		/* current microinstruction */
249	int error;
250
251	struct ppb_xfer *xfer;
252
253	/* microsequence executed to initialize the transfer */
254	struct ppb_microseq initxfer[] = {
255		MS_PTR(MS_UNKNOWN), 	/* set ptr to buffer */
256		MS_SET(MS_UNKNOWN),	/* set transfer size */
257		MS_RET(0)
258	};
259
260	if (ppb->ppb_owner != dev)
261		return (EACCES);
262
263#define INCR_PC (mi ++)
264
265	mi = msq;
266	for (;;) {
267		switch (mi->opcode) {
268		case MS_OP_PUT:
269		case MS_OP_GET:
270
271			/* attempt to choose the best mode for the device */
272			xfer = mode2xfer(dev, mi->opcode);
273
274			/* figure out if we should use ieee1284 code */
275			if (!xfer->loop) {
276				if (mi->opcode == MS_OP_PUT) {
277					if ((error = ppb->ppb_link->adapter->write(
278						ppb->ppb_link->adapter_unit,
279						(char *)mi->arg[0].p,
280						mi->arg[1].i, 0)))
281							goto error;
282
283					INCR_PC;
284					goto next;
285				} else
286					panic("%s: IEEE1284 read not supported", __FUNCTION__);
287			}
288
289			/* XXX should use ppb_MS_init_msq() */
290			initxfer[0].arg[0].p = mi->arg[0].p;
291			initxfer[1].arg[0].i = mi->arg[1].i;
292
293			/* initialize transfer */
294			ppb_MS_microseq(dev, initxfer, &error);
295
296			if (error)
297				goto error;
298
299			/* the xfer microsequence should not contain any
300			 * MS_OP_PUT or MS_OP_GET!
301			 */
302			ppb_MS_microseq(dev, xfer->loop, &error);
303
304			if (error)
305				goto error;
306
307			INCR_PC;
308			break;
309
310                case MS_OP_RET:
311			if (ret)
312				*ret = mi->arg[0].i;	/* return code */
313			return (0);
314                        break;
315
316		default:
317			/* executing microinstructions at ppc level is
318			 * faster. This is the default if the microinstr
319			 * is unknown here
320			 */
321			if ((error = ppb->ppb_link->adapter->exec_microseq(
322						ppb->ppb_link->adapter_unit,
323						&mi)))
324				goto error;
325			break;
326		}
327	next:
328	}
329error:
330	return (error);
331}
332
333