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