ppbus_msq.c revision 1.3 1 /* $NetBSD: ppbus_msq.c,v 1.3 2004/01/21 00:33:37 bjh21 Exp $ */
2
3 /*-
4 * Copyright (c) 1998, 1999 Nicolas Souchu
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: src/sys/dev/ppbus/ppb_msq.c,v 1.9.2.1 2000/05/24 00:20:57 n_hibma Exp $
29 *
30 */
31 #include <machine/stdarg.h>
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35
36 #include <dev/ppbus/ppbus_conf.h>
37 #include <dev/ppbus/ppbus_base.h>
38 #include <dev/ppbus/ppbus_device.h>
39 #include <dev/ppbus/ppbus_msq.h>
40 #include <dev/ppbus/ppbus_var.h>
41
42 /*
43 #include "ppbus_if.h"
44 */
45
46 /*
47 * msq index (see PPBUS_MAX_XFER)
48 * These are device modes
49 */
50 #define COMPAT_MSQ 0x0
51 #define NIBBLE_MSQ 0x1
52 #define PS2_MSQ 0x2
53 #define EPP17_MSQ 0x3
54 #define EPP19_MSQ 0x4
55 #define ECP_MSQ 0x5
56
57 /* Function prototypes */
58 static struct ppbus_xfer * mode2xfer(struct ppbus_softc *,
59 struct ppbus_device_softc *, int);
60
61 /*
62 * Device mode to submsq conversion
63 */
64 static struct ppbus_xfer *
65 mode2xfer(struct ppbus_softc * bus, struct ppbus_device_softc * ppbdev,
66 int opcode)
67 {
68 int index;
69 unsigned int epp;
70 struct ppbus_xfer * table;
71
72 switch (opcode) {
73 case MS_OP_GET:
74 table = ppbdev->get_xfer;
75 break;
76
77 case MS_OP_PUT:
78 table = ppbdev->put_xfer;
79 break;
80
81 default:
82 panic("%s: unknown opcode (%d)", __func__, opcode);
83 }
84
85 /* retrieve the device operating mode */
86 switch (bus->sc_mode) {
87 case PPBUS_COMPATIBLE:
88 index = COMPAT_MSQ;
89 break;
90 case PPBUS_NIBBLE:
91 index = NIBBLE_MSQ;
92 break;
93 case PPBUS_PS2:
94 index = PS2_MSQ;
95 break;
96 case PPBUS_EPP:
97 ppbus_read_ivar(&(bus->sc_dev), PPBUS_IVAR_EPP_PROTO, &epp);
98 switch (epp) {
99 case PPBUS_EPP_1_7:
100 index = EPP17_MSQ;
101 break;
102 case PPBUS_EPP_1_9:
103 index = EPP19_MSQ;
104 break;
105 default:
106 panic("%s: unknown EPP protocol [%u]!", __func__, epp);
107 }
108 break;
109 case PPBUS_ECP:
110 index = ECP_MSQ;
111 break;
112 default:
113 panic("%s: unknown mode (%d)", __func__, ppbdev->mode);
114 }
115
116 return (&table[index]);
117 }
118
119 /*
120 * ppbus_MS_init()
121 *
122 * Initialize device dependent submicrosequence of the current mode
123 *
124 */
125 int
126 ppbus_MS_init(struct device * dev, struct device * ppbdev,
127 struct ppbus_microseq * loop, int opcode)
128 {
129 struct ppbus_softc * bus = (struct ppbus_softc *) dev;
130 struct ppbus_xfer *xfer = mode2xfer(bus, (struct ppbus_device_softc *)
131 ppbdev, opcode);
132
133 xfer->loop = loop;
134
135 return 0;
136 }
137
138 /*
139 * ppbus_MS_exec()
140 *
141 * Execute any microsequence opcode - expensive
142 *
143 */
144 int
145 ppbus_MS_exec(struct device * ppb, struct device * dev,
146 int opcode, union ppbus_insarg param1, union ppbus_insarg param2,
147 union ppbus_insarg param3, int * ret)
148 {
149 struct ppbus_microseq msq[] = {
150 { MS_UNKNOWN, { { MS_UNKNOWN }, { MS_UNKNOWN },
151 { MS_UNKNOWN } } },
152 MS_RET(0)
153 };
154
155 /* initialize the corresponding microseq */
156 msq[0].opcode = opcode;
157 msq[0].arg[0] = param1;
158 msq[0].arg[1] = param2;
159 msq[0].arg[2] = param3;
160
161 /* execute the microseq */
162 return (ppbus_MS_microseq(ppb, dev, msq, ret));
163 }
164
165 /*
166 * ppbus_MS_loop()
167 *
168 * Execute a microseq loop
169 *
170 */
171 int
172 ppbus_MS_loop(struct device * ppb, struct device * dev,
173 struct ppbus_microseq * prolog, struct ppbus_microseq * body,
174 struct ppbus_microseq * epilog, int iter, int * ret)
175 {
176 struct ppbus_microseq loop_microseq[] = {
177 MS_CALL(0), /* execute prolog */
178 MS_SET(MS_UNKNOWN), /* set size of transfer */
179
180 /* loop: */
181 MS_CALL(0), /* execute body */
182 MS_DBRA(-1 /* loop: */),
183
184 MS_CALL(0), /* execute epilog */
185 MS_RET(0)
186 };
187
188 /* initialize the structure */
189 loop_microseq[0].arg[0].p = (void *)prolog;
190 loop_microseq[1].arg[0].i = iter;
191 loop_microseq[2].arg[0].p = (void *)body;
192 loop_microseq[4].arg[0].p = (void *)epilog;
193
194 /* execute the loop */
195 return (ppbus_MS_microseq(ppb, dev, loop_microseq, ret));
196 }
197
198 /*
199 * ppbus_MS_init_msq()
200 *
201 * Initialize a microsequence - see macros in ppbus_msq.h
202 * KNF does not work here, since using '...' requires you use the
203 * standard C way of function definotion.
204 *
205 */
206 int
207 ppbus_MS_init_msq(struct ppbus_microseq * msq, int nbparam, ...)
208 {
209 int i;
210 int param, ins, arg, type;
211 va_list p_list;
212
213 va_start(p_list, nbparam);
214
215 for(i = 0; i < nbparam; i++) {
216 /* retrieve the parameter descriptor */
217 param = va_arg(p_list, int);
218
219 ins = MS_INS(param);
220 arg = MS_ARG(param);
221 type = MS_TYP(param);
222
223 /* check the instruction position */
224 if (arg >= PPBUS_MS_MAXARGS)
225 panic("%s: parameter out of range (0x%x)!", __func__,
226 param);
227
228 #if 0
229 printf("%s: param = %d, ins = %d, arg = %d, type = %d\n",
230 __func__, param, ins, arg, type);
231
232 #endif
233
234 /* properly cast the parameter */
235 switch (type) {
236 case MS_TYP_INT:
237 msq[ins].arg[arg].i = va_arg(p_list, int);
238 break;
239
240 case MS_TYP_CHA:
241 /* XXX was:
242 msq[ins].arg[arg].i = (int)va_arg(p_list, char);
243 which gives warning with gcc 3.3
244 */
245 msq[ins].arg[arg].i = (int)va_arg(p_list, int);
246 break;
247
248 case MS_TYP_PTR:
249 msq[ins].arg[arg].p = va_arg(p_list, void *);
250 break;
251
252 case MS_TYP_FUN:
253 msq[ins].arg[arg].f = va_arg(p_list, void *);
254 break;
255
256 default:
257 panic("%s: unknown parameter (0x%x)!", __func__, param);
258 }
259 }
260
261 return (0);
262 }
263
264 /*
265 * ppbus_MS_microseq()
266 *
267 * Interprete a microsequence. Some microinstructions are executed at adapter
268 * level to avoid function call overhead between ppbus and the adapter
269 */
270 int
271 ppbus_MS_microseq(struct device * dev, struct device * busdev,
272 struct ppbus_microseq * msq, int * ret)
273 {
274 struct ppbus_device_softc * ppbdev = (struct ppbus_device_softc *)
275 busdev;
276 struct ppbus_softc * bus = (struct ppbus_softc *) dev;
277 struct ppbus_microseq * mi; /* current microinstruction */
278 int error;
279 int cnt;
280
281 struct ppbus_xfer * xfer;
282
283 /* microsequence executed to initialize the transfer */
284 struct ppbus_microseq initxfer[] = {
285 MS_PTR(MS_UNKNOWN), /* set ptr to buffer */
286 MS_SET(MS_UNKNOWN), /* set transfer size */
287 MS_RET(0)
288 };
289
290 if(bus->ppbus_owner != busdev) {
291 return (EACCES);
292 }
293
294 #define INCR_PC (mi ++)
295
296 mi = msq;
297 again:
298 for (;;) {
299 switch (mi->opcode) {
300 case MS_OP_PUT:
301 case MS_OP_GET:
302
303 /* attempt to choose the best mode for the device */
304 xfer = mode2xfer(bus, ppbdev, mi->opcode);
305
306 /* figure out if we should use ieee1284 code */
307 if (!xfer->loop) {
308 if (mi->opcode == MS_OP_PUT) {
309 if ((error = ppbus_write(
310 &(bus->sc_dev),
311 (char *)mi->arg[0].p,
312 mi->arg[1].i, 0, &cnt))) {
313 goto error;
314 }
315
316 INCR_PC;
317 goto again;
318 }
319 else {
320 panic("%s: IEEE1284 read not supported",
321 __func__);
322 }
323 }
324
325 /* XXX should use ppbus_MS_init_msq() */
326 initxfer[0].arg[0].p = mi->arg[0].p;
327 initxfer[1].arg[0].i = mi->arg[1].i;
328
329 /* initialize transfer */
330 ppbus_MS_microseq(dev, busdev, initxfer, &error);
331
332 if (error)
333 goto error;
334
335 /* the xfer microsequence should not contain any
336 * MS_OP_PUT or MS_OP_GET!
337 */
338 ppbus_MS_microseq(dev, busdev, xfer->loop, &error);
339
340 if (error)
341 goto error;
342
343 INCR_PC;
344 break;
345
346 case MS_OP_RET:
347 if (ret)
348 *ret = mi->arg[0].i; /* return code */
349 return (0);
350 break;
351
352 default:
353 /* executing microinstructions at ppc level is
354 * faster. This is the default if the microinstr
355 * is unknown here
356 */
357 if((error =
358 bus->ppbus_exec_microseq(
359 (struct device *)bus, &mi))) {
360
361 goto error;
362 }
363 break;
364 }
365 }
366 error:
367 return (error);
368 }
369
370