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