1 /* $NetBSD: qv.c,v 1.11 2002/10/23 09:12:34 jdolecek Exp $ */ 2 3 /*- 4 * Copyright (c) 1988 5 * The Regents of the University of California. All rights reserved. 6 * (c) UNIX System Laboratories, Inc. 7 * All or some portions of this file are derived from material licensed 8 * to the University of California by American Telephone and Telegraph 9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 * the permission of UNIX System Laboratories, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * @(#)qv.c 7.2 (Berkeley) 1/21/94 41 */ 42 43 /* 44 * derived from: @(#)qv.c 1.8 (ULTRIX) 8/21/85 45 */ 46 47 /************************************************************************ 48 * * 49 * Copyright (c) 1985 by * 50 * Digital Equipment Corporation, Maynard, MA * 51 * All rights reserved. * 52 * * 53 * This software is furnished under a license and may be used and * 54 * copied only in accordance with the terms of such license and * 55 * with the inclusion of the above copyright notice. This * 56 * software or any other copies thereof may not be provided or * 57 * otherwise made available to any other person. No title to and * 58 * ownership of the software is hereby transferred. * 59 * * 60 * This software is derived from software received from the * 61 * University of California, Berkeley, and from Bell * 62 * Laboratories. Use, duplication, or disclosure is subject to * 63 * restrictions under license agreements with University of * 64 * California and with AT&T. * 65 * * 66 * The information in this software is subject to change without * 67 * notice and should not be construed as a commitment by Digital * 68 * Equipment Corporation. * 69 * * 70 * Digital assumes no responsibility for the use or reliability * 71 * of its software on equipment which is not supplied by Digital. * 72 * * 73 ************************************************************************ 74 * 75 * This driver provides glass tty functionality to the qvss. It is a strange 76 * device in that it supports three subchannels. The first being the asr, 77 * the second being a channel that intercepts the chars headed for the screen 78 * ( like a pseudo tty ) and the third being a source of mouse state changes. 79 * NOTE: the second is conditional on #ifdef CONS_HACK in this version 80 * of the driver, as it's a total crock. 81 * 82 * There may be one and only one qvss in the system. This restriction is based 83 * on the inability to map more than one at a time. This restriction will 84 * exist until the kernel has shared memory services. This driver therefore 85 * support a single unit. No attempt was made to have it service more. 86 * 87 * (this belongs in sccs - not here) 88 * 89 * 02 Aug 85 -- rjl 90 * Changed the names of the special setup routines so that the system 91 * can have a qvss or a qdss system console. 92 * 93 * 03 Jul 85 -- rjl 94 * Added a check for virtual mode in qvputc so that the driver 95 * doesn't crash while in a dump which is done in physical mode. 96 * 97 * 10 Apr 85 -- jg 98 * Well, our theory about keyboard handling was wrong; most of the 99 * keyboard is in autorepeat, down mode. These changes are to make 100 * the qvss work the same as the Vs100, which is not necessarily 101 * completely correct, as some chord usage may fail. But since we 102 * can't easily change the Vs100, we might as well propagate the 103 * problem to another device. There are also changes for screen and 104 * mouse accellaration. 105 * 106 * 27 Mar 85 -- rjl 107 * MicroVAX-II systems have interval timers that interrupt at ipl4. 108 * Everything else is higher and thus causes us to miss clock ticks. The 109 * problem isn't severe except in the case of a device like this one that 110 * generates lots of interrupts. We aren't willing to make this change to 111 * all device drivers but it seems acceptable in this case. 112 * 113 * 3 Dec 84 -- jg 114 * To continue the tradition of building a better mouse trap, this 115 * driver has been extended to form Vs100 style event queues. If the 116 * mouse device is open, the keyboard events are intercepted and put 117 * into the shared memory queue. Unfortunately, we are ending up with 118 * one of the longest Unix device drivers. Sigh.... 119 * 120 * 20 Nov 84 -- rjl 121 * As a further complication this driver is required to function as the 122 * virtual system console. This code runs before and during auto- 123 * configuration and therefore is require to have a second path for setup. 124 * It is futher constrained to have a character output routine that 125 * is not dependant on the interrupt system. 126 * 127 */ 128 129 130 #include "qv.h" 131 #if NQV > 0 132 133 #include "../include/pte.h" 134 135 #include "sys/param.h" 136 #include "sys/conf.h" 137 #include "sys/user.h" 138 #include "qvioctl.h" 139 #include "sys/tty.h" 140 #include "sys/buf.h" 141 #include "sys/vm.h" 142 #include "sys/file.h" 143 #include "sys/uio.h" 144 #include "sys/kernel.h" 145 #include "sys/syslog.h" 146 #include "../include/cpu.h" 147 #include "../include/mtpr.h" 148 #include "ubareg.h" 149 #include "ubavar.h" 150 151 #define CONS_HACK 152 153 struct uba_device *qvinfo[NQV]; 154 155 struct tty qv_tty[NQV*4]; 156 157 #define nNQV NQV 158 int nqv = NQV*4; 159 160 /* 161 * Definition of the driver for the auto-configuration program. 162 */ 163 int qvprobe(), qvattach(), qvkint(), qvvint(); 164 u_short qvstd[] = { 0 }; 165 struct uba_driver qvdriver = 166 { qvprobe, 0, qvattach, 0, qvstd, "qv", qvinfo }; 167 168 extern char qvmem[][512*VAX_NBPG]; 169 extern struct pte QVmap[][512]; 170 171 /* 172 * Local variables for the driver. Initialized for 15' screen 173 * so that it can be used during the boot process. 174 */ 175 176 #define QVWAITPRI (PZERO+1) 177 178 #define QVKEYBOARD 0 /* minor 0, keyboard/glass tty */ 179 #define QVPCONS 1 /* minor 1, console interceptor XXX */ 180 #define QVMOUSECHAN 2 /* minor 2, mouse */ 181 #define QVSPARE 3 /* unused */ 182 #define QVCHAN(unit) ((unit) & 03) 183 /* 184 * v_putc is the switch that is used to redirect the console cnputc to the 185 * virtual console vputc. consops is used to redirect the console 186 * device to the qvss console. 187 */ 188 extern (*v_putc)(); 189 extern struct cdevsw *consops; 190 /* 191 * qv_def_scrn is used to select the appropriate tables. 0=15 inch 1=19 inch, 192 * 2 = uVAXII. 193 */ 194 int qv_def_scrn = 2; 195 196 #define QVMAXEVQ 64 /* must be power of 2 */ 197 #define EVROUND(x) ((x) & (QVMAXEVQ - 1)) 198 199 /* 200 * Screen parameters 15 & 19 inch monitors. These determine the max size in 201 * pixel and character units for the display and cursor positions. 202 * Notice that the mouse defaults to original square algorithm, but X 203 * will change to its defaults once implemented. 204 */ 205 struct qv_info *qv_scn; 206 struct qv_info qv_scn_defaults[] = { 207 {0, {0, 0}, 0, {0, 0}, 0, 0, 30, 80, 768, 480, 768-16, 480-16, 208 0, 0, 0, 0, 0, QVMAXEVQ, 0, 0, {0, 0}, {0, 0, 0, 0}, 2, 4}, 209 {0, {0, 0}, 0, {0, 0}, 0, 0, 55, 120, 960, 864, 960-16, 864-16, 210 0, 0, 0, 0, 0, QVMAXEVQ, 0, 0, {0, 0}, {0, 0, 0, 0}, 2, 4}, 211 {0, {0, 0}, 0, {0, 0}, 0, 0, 56, 120,1024, 864,1024-16, 864-16, 212 0, 0, 0, 0, 0, QVMAXEVQ, 0, 0, {0, 0}, {0, 0, 0, 0}, 2, 4} 213 }; 214 215 /* 216 * Screen controller initialization parameters. The definations and use 217 * of these parameters can be found in the Motorola 68045 crtc specs. In 218 * essence they set the display parameters for the chip. The first set is 219 * for the 15" screen and the second is for the 19" separate sync. There 220 * is also a third set for a 19" composite sync monitor which we have not 221 * tested and which is not supported. 222 */ 223 static short qv_crt_parms[][16] = { 224 { 31, 25, 27, 0142, 31, 13, 30, 31, 4, 15, 040, 0, 0, 0, 0, 0 }, 225 /* VR100*/ { 39, 30, 32, 0262, 55, 5, 54, 54, 4, 15, 040, 0, 0, 0, 0, 0 }, 226 /* VR260*/ { 39, 32, 33, 0264, 56, 5, 54, 54, 4, 15, 040, 0, 0, 0, 0, 0}, 227 }; 228 229 /* 230 * Screen parameters 231 */ 232 struct qv_info *qv_scn; 233 int maxqvmem = 254*1024 - sizeof(struct qv_info) - QVMAXEVQ*sizeof(vsEvent); 234 235 /* 236 * Keyboard state 237 */ 238 struct qv_keyboard { 239 int shift; /* state variables */ 240 int cntrl; 241 int lock; 242 char last; /* last character */ 243 } qv_keyboard; 244 245 short divdefaults[15] = { LK_DOWN, /* 0 doesn't exist */ 246 LK_AUTODOWN, LK_AUTODOWN, LK_AUTODOWN, LK_DOWN, 247 LK_UPDOWN, LK_UPDOWN, LK_AUTODOWN, LK_AUTODOWN, 248 LK_AUTODOWN, LK_AUTODOWN, LK_AUTODOWN, LK_AUTODOWN, 249 LK_DOWN, LK_AUTODOWN }; 250 251 short kbdinitstring[] = { /* reset any random keyboard stuff */ 252 LK_AR_ENABLE, /* we want autorepeat by default */ 253 LK_CL_ENABLE, /* keyclick */ 254 0x84, /* keyclick volume */ 255 LK_KBD_ENABLE, /* the keyboard itself */ 256 LK_BELL_ENABLE, /* keyboard bell */ 257 0x84, /* bell volume */ 258 LK_LED_DISABLE, /* keyboard leds */ 259 LED_ALL }; 260 #define KBD_INIT_LENGTH sizeof(kbdinitstring)/sizeof(short) 261 262 #define TOY ((time.tv_sec * 100) + (time.tv_usec / 10000)) 263 264 int qv_ipl_lo = 1; /* IPL low flag */ 265 int mouseon = 0; /* mouse channel is enabled when 1*/ 266 struct proc *qvrsel; /* process waiting for select */ 267 268 int qvstart(), qvputc(), ttrstrt(); 269 270 /* 271 * Keyboard translation and font tables 272 */ 273 extern u_short q_key[], q_shift_key[], q_cursor[]; 274 extern char *q_special[], q_font[]; 275 276 dev_type_open(qvopen); 277 dev_type_close(qvclose); 278 dev_type_read(qvread); 279 dev_type_write(qvwrite); 280 dev_type_ioctl(qvioctl); 281 dev_type_stop(qvstop); 282 dev_type_poll(qvpoll); 283 dev_type_kqfilter(qvkqfilter); 284 285 const struct cdevsw qv_cdevsw = { 286 qvopen, qvclose, qvread, qvwrite, qvioctl, 287 qvstop, notty, qvpoll, nommap, qvkqfilter, 288 }; 289 290 /* 291 * See if the qvss will interrupt. 292 */ 293 294 /*ARGSUSED*/ 295 qvprobe(reg, ctlr) 296 caddr_t reg; 297 int ctlr; 298 { 299 register int br, cvec; /* these are ``value-result'' */ 300 register struct qvdevice *qvaddr = (struct qvdevice *)reg; 301 static int tvec, ovec; 302 303 #ifdef lint 304 br = 0; cvec = br; br = cvec; 305 qvkint(0); qvvint(0); 306 #endif 307 /* 308 * Allocate the next two vectors 309 */ 310 tvec = 0360; 311 ovec = cvec; 312 /* 313 * Turn on the keyboard and vertical interrupt vectors. 314 */ 315 qvaddr->qv_intcsr = 0; /* init the interrupt controler */ 316 qvaddr->qv_intcsr = 0x40; /* reset irr */ 317 qvaddr->qv_intcsr = 0x80; /* specify individual vectors */ 318 qvaddr->qv_intcsr = 0xc0; /* preset autoclear data */ 319 qvaddr->qv_intdata = 0xff; /* all setup as autoclear */ 320 321 qvaddr->qv_intcsr = 0xe0; /* preset vector address 1 */ 322 qvaddr->qv_intdata = tvec; /* give it the keyboard vector */ 323 qvaddr->qv_intcsr = 0x28; /* enable tx/rx interrupt */ 324 325 qvaddr->qv_intcsr = 0xe1; /* preset vector address 2 */ 326 qvaddr->qv_intdata = tvec+4; /* give it the vertical sysnc */ 327 qvaddr->qv_intcsr = 0x29; /* enable */ 328 329 qvaddr->qv_intcsr = 0xa1; /* arm the interrupt ctrl */ 330 331 qvaddr->qv_uartcmd = 0x15; /* set mode pntr/enable rx/tx */ 332 qvaddr->qv_uartmode = 0x17; /* noparity, 8-bit */ 333 qvaddr->qv_uartmode = 0x07; /* 1 stop bit */ 334 qvaddr->qv_uartstatus = 0x99; /* 4800 baud xmit/recv */ 335 qvaddr->qv_uartintstatus = 2; /* enable recv interrupts */ 336 337 qvaddr->qv_csr |= QV_INT_ENABLE | QV_CUR_MODE; 338 339 DELAY(10000); 340 341 qvaddr->qv_csr &= ~QV_INT_ENABLE; 342 343 /* 344 * If the qvss did interrupt it was the second vector not 345 * the first so we have to return the first so that they 346 * will be setup properly 347 */ 348 if( ovec == cvec ) { 349 return 0; 350 } else 351 cvec -= 4; 352 return (sizeof (struct qvdevice)); 353 } 354 355 /* 356 * Routine called to attach a qv. 357 */ 358 qvattach(ui) 359 struct uba_device *ui; 360 { 361 362 /* 363 * If not the console then we have to setup the screen 364 */ 365 if (v_putc != qvputc || ui->ui_unit != 0) 366 (void)qv_setup((struct qvdevice *)ui->ui_addr, ui->ui_unit, 1); 367 else 368 qv_scn->qvaddr = (struct qvdevice *)ui->ui_addr; 369 } 370 371 372 /*ARGSUSED*/ 373 int 374 qvopen(dev, flag, mode, p) 375 dev_t dev; 376 int flag, mode; 377 struct proc *p; 378 { 379 register struct tty *tp; 380 register int unit, qv; 381 register struct qvdevice *qvaddr; 382 register struct uba_device *ui; 383 register struct qv_info *qp = qv_scn; 384 385 unit = minor(dev); 386 qv = unit >> 2; 387 if (unit >= nqv || (ui = qvinfo[qv])== 0 || ui->ui_alive == 0) 388 return (ENXIO); 389 if (QVCHAN(unit) == QVSPARE 390 #ifndef CONS_HACK 391 || QVCHAN(unit) == QVPCONS 392 #endif 393 ) 394 return (ENODEV); 395 tp = &qv_tty[unit]; 396 if (tp->t_state&TS_XCLUDE && u.u_uid!=0) 397 return (EBUSY); 398 qvaddr = (struct qvdevice *)ui->ui_addr; 399 qv_scn->qvaddr = qvaddr; 400 tp->t_addr = (caddr_t)qvaddr; 401 tp->t_oproc = qvstart; 402 403 if ((tp->t_state&TS_ISOPEN) == 0) { 404 ttychars(tp); 405 tp->t_state = TS_ISOPEN|TS_CARR_ON; 406 tp->t_ispeed = B9600; 407 tp->t_ospeed = B9600; 408 if( QVCHAN(unit) == QVKEYBOARD ) { 409 /* make sure keyboard is always back to default */ 410 qvkbdreset(); 411 qvaddr->qv_csr |= QV_INT_ENABLE; 412 tp->t_iflag = TTYDEF_IFLAG; 413 tp->t_oflag = TTYDEF_OFLAG; 414 tp->t_lflag = TTYDEF_LFLAG; 415 tp->t_cflag = TTYDEF_CFLAG; 416 } 417 /* XXX ?why? else 418 tp->t_flags = RAW; 419 */ 420 } 421 /* 422 * Process line discipline specific open if its not the 423 * mouse channel. For the mouse we init the ring ptr's. 424 */ 425 if( QVCHAN(unit) != QVMOUSECHAN ) 426 return ((*tp->t_linesw->l_open)(dev, tp)); 427 else { 428 mouseon = 1; 429 /* set up event queue for later */ 430 qp->ibuff = (vsEvent *)qp - QVMAXEVQ; 431 qp->iqsize = QVMAXEVQ; 432 qp->ihead = qp->itail = 0; 433 return 0; 434 } 435 436 return (0); 437 } 438 439 /* 440 * Close a QVSS line. 441 */ 442 /*ARGSUSED*/ 443 int 444 qvclose(dev, flag, mode, p) 445 dev_t dev; 446 int flag, mode; 447 struct proc *p; 448 { 449 register struct tty *tp; 450 register unit; 451 register struct qvdevice *qvaddr; 452 int error; 453 454 unit = minor(dev); 455 tp = &qv_tty[unit]; 456 457 /* 458 * If this is the keyboard unit (0) shutdown the 459 * interface. 460 */ 461 qvaddr = (struct qvdevice *)tp->t_addr; 462 if (QVCHAN(unit) == QVKEYBOARD ) 463 qvaddr->qv_csr &= ~QV_INT_ENABLE; 464 465 /* 466 * If unit is not the mouse channel call the line disc. 467 * otherwise clear the state flag, and put the keyboard into down/up. 468 */ 469 if (QVCHAN(unit) != QVMOUSECHAN) { 470 (*tp->t_linesw->l_close)(tp, flag); 471 error = ttyclose(tp); 472 } else { 473 mouseon = 0; 474 qv_init( qvaddr ); 475 error = 0; 476 } 477 tp->t_state = 0; 478 return (error); 479 } 480 481 int 482 qvread(dev, uio, flag) 483 dev_t dev; 484 struct uio *uio; 485 int flag; 486 { 487 register struct tty *tp; 488 int unit = minor( dev ); 489 490 if (QVCHAN(unit) != QVMOUSECHAN) { 491 tp = &qv_tty[unit]; 492 return ((*tp->t_linesw->l_read)(tp, uio)); 493 } 494 return (ENXIO); 495 } 496 497 int 498 qvwrite(dev, uio, flag) 499 dev_t dev; 500 struct uio *uio; 501 int flag; 502 { 503 register struct tty *tp; 504 int unit = minor( dev ); 505 506 /* 507 * If this is the mouse we simply fake the i/o, otherwise 508 * we let the line disp. handle it. 509 */ 510 if (QVCHAN(unit) == QVMOUSECHAN) { 511 uio->uio_offset = uio->uio_resid; 512 uio->uio_resid = 0; 513 return 0; 514 } 515 tp = &qv_tty[unit]; 516 return ((*tp->t_linesw->l_write)(tp, uio)); 517 } 518 519 int 520 qvpoll(dev, events, p) 521 dev_t dev; 522 int events; 523 struct proc *p; 524 { 525 register struct tty *tp; 526 int unit = minor( dev ); 527 528 /* 529 * XXX Should perform similar checks to deprecated `qvselect()' 530 */ 531 tp = &qv_tty[unit]; 532 return ((*tp->t_linesw->l_poll)(tp, events, p)); 533 } 534 535 /* 536 * XXX Is qvselect() even useful now? 537 * This driver looks to have suffered some serious bit-rot... 538 */ 539 540 /* 541 * Mouse activity select routine 542 */ 543 qvselect(dev, rw) 544 dev_t dev; 545 { 546 register int s = spl5(); 547 register struct qv_info *qp = qv_scn; 548 549 if( QVCHAN(minor(dev)) == QVMOUSECHAN ) 550 switch(rw) { 551 case FREAD: /* if events okay */ 552 if(qp->ihead != qp->itail) { 553 splx(s); 554 return(1); 555 } 556 qvrsel = u.u_procp; 557 splx(s); 558 return(0); 559 default: /* can never write */ 560 splx(s); 561 return(0); 562 } 563 else { 564 splx(s); 565 return( ttselect(dev, rw) ); 566 } 567 /*NOTREACHED*/ 568 } 569 570 /* 571 * QVSS keyboard interrupt. 572 */ 573 qvkint(qv) 574 int qv; 575 { 576 struct tty *tp; 577 register c; 578 struct uba_device *ui; 579 register int key; 580 register int i; 581 582 ui = qvinfo[qv]; 583 if (ui == 0 || ui->ui_alive == 0) 584 return; 585 tp = &qv_tty[qv<<2]; 586 /* 587 * Get a character from the keyboard. 588 */ 589 key = ((struct qvdevice *)ui->ui_addr)->qv_uartdata & 0xff; 590 if( mouseon == 0) { 591 /* 592 * Check for various keyboard errors 593 */ 594 if( key == LK_POWER_ERROR || key == LK_KDOWN_ERROR || 595 key == LK_INPUT_ERROR || key == LK_OUTPUT_ERROR) { 596 log(LOG_ERR, 597 "qv%d: Keyboard error, code = %x\n",qv,key); 598 return; 599 } 600 if( key < LK_LOWEST ) return; 601 /* 602 * See if its a state change key 603 */ 604 switch ( key ) { 605 case LOCK: 606 qv_keyboard.lock ^= 0xffff; /* toggle */ 607 if( qv_keyboard.lock ) 608 qv_key_out( LK_LED_ENABLE ); 609 else 610 qv_key_out( LK_LED_DISABLE ); 611 qv_key_out( LED_3 ); 612 return; 613 case SHIFT: 614 qv_keyboard.shift ^= 0xffff; 615 return; 616 case CNTRL: 617 qv_keyboard.cntrl ^= 0xffff; 618 return; 619 case ALLUP: 620 qv_keyboard.cntrl = qv_keyboard.shift = 0; 621 return; 622 case REPEAT: 623 c = qv_keyboard.last; 624 break; 625 default: 626 /* 627 * Test for control characters. If set, see if the character 628 * is elligible to become a control character. 629 */ 630 if( qv_keyboard.cntrl ) { 631 c = q_key[ key ]; 632 if( c >= ' ' && c <= '~' ) 633 c &= 0x1f; 634 } else if( qv_keyboard.lock || qv_keyboard.shift ) 635 c = q_shift_key[ key ]; 636 else 637 c = q_key[ key ]; 638 break; 639 } 640 641 qv_keyboard.last = c; 642 643 /* 644 * Check for special function keys 645 */ 646 if( c & 0x80 ) { 647 register char *string; 648 string = q_special[ c & 0x7f ]; 649 while( *string ) 650 (*tp->t_linesw->l_rint)(*string++, tp); 651 } else 652 (*tp->t_linesw->l_rint)(c, tp); 653 } else { 654 /* 655 * Mouse channel is open put it into the event queue 656 * instead. 657 */ 658 register struct qv_info *qp = qv_scn; 659 register vsEvent *vep; 660 661 if ((i = EVROUND(qp->itail+1)) == qp->ihead) 662 return; 663 vep = &qp->ibuff[qp->itail]; 664 vep->vse_direction = VSE_KBTRAW; 665 vep->vse_type = VSE_BUTTON; 666 vep->vse_device = VSE_DKB; 667 vep->vse_x = qp->mouse.x; 668 vep->vse_y = qp->mouse.y; 669 vep->vse_time = TOY; 670 vep->vse_key = key; 671 qp->itail = i; 672 if(qvrsel) { 673 selwakeup(qvrsel,0); 674 qvrsel = 0; 675 } 676 } 677 } 678 679 /* 680 * Ioctl for QVSS. 681 */ 682 /*ARGSUSED*/ 683 int 684 qvioctl(dev, cmd, data, flag, p) 685 dev_t dev; 686 u_long cmd; 687 register caddr_t data; 688 int flag; 689 struct proc *p; 690 { 691 register struct tty *tp; 692 register int unit = minor(dev); 693 register struct qv_info *qp = qv_scn; 694 register struct qv_kpcmd *qk; 695 register unsigned char *cp; 696 int error; 697 698 /* 699 * Check for and process qvss specific ioctl's 700 */ 701 switch( cmd ) { 702 case QIOCGINFO: /* return screen info */ 703 bcopy((caddr_t)qp, data, sizeof (struct qv_info)); 704 break; 705 706 case QIOCSMSTATE: /* set mouse state */ 707 qp->mouse = *((vsCursor *)data); 708 qv_pos_cur( qp->mouse.x, qp->mouse.y ); 709 break; 710 711 case QIOCINIT: /* init screen */ 712 qv_init( qp->qvaddr ); 713 break; 714 715 case QIOCKPCMD: 716 qk = (struct qv_kpcmd *)data; 717 if(qk->nbytes == 0) qk->cmd |= 0200; 718 if(mouseon == 0) qk->cmd |= 1; /* no mode changes */ 719 qv_key_out(qk->cmd); 720 cp = &qk->par[0]; 721 while(qk->nbytes-- > 0) { /* terminate parameters */ 722 if(qk->nbytes <= 0) *cp |= 0200; 723 qv_key_out(*cp++); 724 } 725 break; 726 case QIOCADDR: /* get struct addr */ 727 *(struct qv_info **) data = qp; 728 break; 729 default: /* not ours ?? */ 730 tp = &qv_tty[unit]; 731 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag); 732 if (error != EPASSTHROUGH) 733 return (error); 734 return ttioctl(tp, cmd, data, flag); 735 break; 736 } 737 return (0); 738 } 739 /* 740 * Initialize the screen and the scanmap 741 */ 742 qv_init(qvaddr) 743 struct qvdevice *qvaddr; 744 { 745 register short *scanline; 746 register int i; 747 register short scan; 748 register char *ptr; 749 register struct qv_info *qp = qv_scn; 750 751 /* 752 * Clear the bit map 753 */ 754 for( i=0 , ptr = qp->bitmap ; i<240 ; i += 2 , ptr += 2048) 755 bzero( ptr, 2048 ); 756 /* 757 * Reinitialize the scanmap 758 */ 759 scan = qvaddr->qv_csr & QV_MEM_BANK; 760 scanline = qp->scanmap; 761 for(i = 0 ; i < qp->max_y ; i++ ) 762 *scanline++ = scan++; 763 764 /* 765 * Home the cursor 766 */ 767 qp->row = qp->col = 0; 768 769 /* 770 * Reset the cursor to the default type. 771 */ 772 for( i=0 ; i<16 ; i++ ) 773 qp->cursorbits[i] = q_cursor[i]; 774 qvaddr->qv_csr |= QV_CUR_MODE; 775 /* 776 * Reset keyboard to default state. 777 */ 778 qvkbdreset(); 779 } 780 781 qvreset() 782 { 783 } 784 qvkbdreset() 785 { 786 register int i; 787 qv_key_out(LK_DEFAULTS); 788 for( i=1 ; i < 15 ; i++ ) 789 qv_key_out( divdefaults[i] | (i<<3)); 790 for (i = 0; i < KBD_INIT_LENGTH; i++) 791 qv_key_out(kbdinitstring[i]); 792 } 793 794 #define abs(x) (((x) > 0) ? (x) : (-(x))) 795 /* 796 * QVSS vertical sync interrupt 797 */ 798 qvvint(qv) 799 int qv; 800 { 801 extern int selwait; 802 register struct qvdevice *qvaddr; 803 struct uba_device *ui; 804 register struct qv_info *qp = qv_scn; 805 int unit; 806 struct tty *tp0; 807 int i; 808 register int j; 809 /* 810 * Mouse state info 811 */ 812 static ushort omouse = 0, nmouse = 0; 813 static char omx=0, omy=0, mx=0, my=0, om_switch=0, m_switch=0; 814 register int dx, dy; 815 816 /* 817 * Test and set the qv_ipl_lo flag. If the result is not zero then 818 * someone else must have already gotten here. 819 */ 820 if( --qv_ipl_lo ) 821 return; 822 (void)spl4(); 823 ui = qvinfo[qv]; 824 unit = qv<<2; 825 qvaddr = (struct qvdevice *)ui->ui_addr; 826 tp0 = &qv_tty[QVCHAN(unit) + QVMOUSECHAN]; 827 /* 828 * See if the mouse has moved. 829 */ 830 if( omouse != (nmouse = qvaddr->qv_mouse) ) { 831 omouse = nmouse; 832 mx = nmouse & 0xff; 833 my = nmouse >> 8; 834 dy = my - omy; omy = my; 835 dx = mx - omx; omx = mx; 836 if( dy < 50 && dy > -50 && dx < 50 && dx > -50 ) { 837 register vsEvent *vep; 838 if( qp->mscale < 0 ) { /* Ray Lanza's original */ 839 if( dy < 0 ) 840 dy = -( dy * dy ); 841 else 842 dy *= dy; 843 if( dx < 0 ) 844 dx = -( dx * dx ); 845 else 846 dx *= dx; 847 } 848 else { /* Vs100 style, see WGA spec */ 849 int thresh = qp->mthreshold; 850 int scale = qp->mscale; 851 if( abs(dx) > thresh ) { 852 if ( dx < 0 ) 853 dx = (dx + thresh)*scale - thresh; 854 else 855 dx = (dx - thresh)*scale + thresh; 856 } 857 if( abs(dy) > thresh ) { 858 if ( dy < 0 ) 859 dy = (dy + thresh)*scale - thresh; 860 else 861 dy = (dy - thresh)*scale + thresh; 862 } 863 } 864 qp->mouse.x += dx; 865 qp->mouse.y -= dy; 866 if( qp->mouse.x < 0 ) 867 qp->mouse.x = 0; 868 if( qp->mouse.y < 0 ) 869 qp->mouse.y = 0; 870 if( qp->mouse.x > qp->max_cur_x ) 871 qp->mouse.x = qp->max_cur_x; 872 if( qp->mouse.y > qp->max_cur_y ) 873 qp->mouse.y = qp->max_cur_y; 874 if( tp0->t_state & TS_ISOPEN ) 875 qv_pos_cur( qp->mouse.x, qp->mouse.y ); 876 if (qp->mouse.y < qp->mbox.bottom && 877 qp->mouse.y >= qp->mbox.top && 878 qp->mouse.x < qp->mbox.right && 879 qp->mouse.x >= qp->mbox.left) goto switches; 880 qp->mbox.bottom = 0; /* trash box */ 881 if (EVROUND(qp->itail+1) == qp->ihead) 882 goto switches; 883 i = EVROUND(qp->itail - 1); 884 if ((qp->itail != qp->ihead) && (i != qp->ihead)) { 885 vep = & qp->ibuff[i]; 886 if(vep->vse_type == VSE_MMOTION) { 887 vep->vse_x = qp->mouse.x; 888 vep->vse_y = qp->mouse.y; 889 goto switches; 890 } 891 } 892 /* put event into queue and do select */ 893 vep = & qp->ibuff[qp->itail]; 894 vep->vse_type = VSE_MMOTION; 895 vep->vse_time = TOY; 896 vep->vse_x = qp->mouse.x; 897 vep->vse_y = qp->mouse.y; 898 qp->itail = EVROUND(qp->itail+1); 899 } 900 } 901 /* 902 * See if mouse switches have changed. 903 */ 904 switches:if( om_switch != ( m_switch = (qvaddr->qv_csr & QV_MOUSE_ANY) >> 8 ) ) { 905 qp->mswitches = ~m_switch & 0x7; 906 for (j = 0; j < 3; j++) { /* check each switch */ 907 register vsEvent *vep; 908 if ( ((om_switch>>j) & 1) == ((m_switch>>j) & 1) ) 909 continue; 910 /* check for room in the queue */ 911 if ((i = EVROUND(qp->itail+1)) == qp->ihead) return; 912 /* put event into queue and do select */ 913 vep = &qp->ibuff[qp->itail]; 914 vep->vse_type = VSE_BUTTON; 915 vep->vse_key = 2 - j; 916 vep->vse_direction = VSE_KBTDOWN; 917 if ( (m_switch >> j) & 1) 918 vep->vse_direction = VSE_KBTUP; 919 vep->vse_device = VSE_MOUSE; 920 vep->vse_time = TOY; 921 vep->vse_x = qp->mouse.x; 922 vep->vse_y = qp->mouse.y; 923 } 924 qp->itail = i; 925 om_switch = m_switch; 926 qp->mswitches = m_switch; 927 } 928 /* if we have proc waiting, and event has happened, wake him up */ 929 if(qvrsel && (qp->ihead != qp->itail)) { 930 selwakeup(qvrsel,0); 931 qvrsel = 0; 932 } 933 /* 934 * Okay we can take another hit now 935 */ 936 qv_ipl_lo = 1; 937 } 938 939 /* 940 * Start transmission 941 */ 942 qvstart(tp) 943 register struct tty *tp; 944 { 945 register int unit, c; 946 register struct tty *tp0; 947 int s; 948 949 unit = minor(tp->t_dev); 950 #ifdef CONS_HACK 951 tp0 = &qv_tty[(unit&0xfc)+QVPCONS]; 952 #endif 953 unit = QVCHAN(unit); 954 955 s = spl5(); 956 /* 957 * If it's currently active, or delaying, no need to do anything. 958 */ 959 if (tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) 960 goto out; 961 /* 962 * Display chars until the queue is empty, if the second subchannel 963 * is open direct them there. Drop characters from subchannels other 964 * than 0 on the floor. 965 */ 966 967 while( tp->t_outq.c_cc ) { 968 c = getc(&tp->t_outq); 969 if (unit == QVKEYBOARD) 970 #ifdef CONS_HACK 971 if( tp0->t_state & TS_ISOPEN ){ 972 (*linesw[tp0->t_line].l_rint)(c, tp0); 973 } else 974 #endif 975 qvputchar( c & 0xff ); 976 } 977 /* 978 * Position the cursor to the next character location. 979 */ 980 qv_pos_cur( qv_scn->col*8, qv_scn->row*15 ); 981 982 /* 983 * If there are sleepers, and output has drained below low 984 * water mark, wake up the sleepers. 985 */ 986 if ( tp->t_outq.c_cc<= tp->t_lowat ) { 987 if (tp->t_state&TS_ASLEEP){ 988 tp->t_state &= ~TS_ASLEEP; 989 wakeup((caddr_t)&tp->t_outq); 990 } 991 } 992 tp->t_state &= ~TS_BUSY; 993 out: 994 splx(s); 995 } 996 997 /* 998 * Stop output on a line, e.g. for ^S/^Q or output flush. 999 */ 1000 /*ARGSUSED*/ 1001 void 1002 qvstop(tp, flag) 1003 register struct tty *tp; 1004 int flag; 1005 { 1006 register int s; 1007 1008 /* 1009 * Block input/output interrupts while messing with state. 1010 */ 1011 s = spl5(); 1012 if (tp->t_state & TS_BUSY) { 1013 if ((tp->t_state&TS_TTSTOP)==0) { 1014 tp->t_state |= TS_FLUSH; 1015 } else 1016 tp->t_state &= ~TS_BUSY; 1017 } 1018 splx(s); 1019 } 1020 1021 qvputc(c) 1022 char c; 1023 { 1024 qvputchar(c); 1025 if (c == '\n') 1026 qvputchar('\r'); 1027 } 1028 1029 /* 1030 * Routine to display a character on the screen. The model used is a 1031 * glass tty. It is assummed that the user will only use this emulation 1032 * during system boot and that the screen will be eventually controlled 1033 * by a window manager. 1034 * 1035 */ 1036 qvputchar( c ) 1037 register char c; 1038 { 1039 1040 register char *b_row, *f_row; 1041 register int i; 1042 register short *scanline; 1043 register int ote = 128; 1044 register struct qv_info *qp = qv_scn; 1045 1046 /* 1047 * This routine may be called in physical mode by the dump code 1048 * so we check and punt if that's the case. 1049 */ 1050 if( (mfpr(MAPEN) & 1) == 0 ) 1051 return; 1052 1053 c &= 0x7f; 1054 1055 switch ( c ) { 1056 case '\t': /* tab */ 1057 for( i = 8 - (qp->col & 0x7) ; i > 0 ; i-- ) 1058 qvputchar( ' ' ); 1059 break; 1060 1061 case '\r': /* return */ 1062 qp->col = 0; 1063 break; 1064 1065 case '\010': /* backspace */ 1066 if( --qp->col < 0 ) 1067 qp->col = 0; 1068 break; 1069 1070 case '\n': /* linefeed */ 1071 if( qp->row+1 >= qp->max_row ) 1072 qvscroll(); 1073 else 1074 qp->row++; 1075 /* 1076 * Position the cursor to the next character location. 1077 */ 1078 qv_pos_cur( qp->col*8, qp->row*15 ); 1079 break; 1080 1081 case '\007': /* bell */ 1082 /* 1083 * We don't do anything to the keyboard until after 1084 * autoconfigure. 1085 */ 1086 if( qp->qvaddr ) 1087 qv_key_out( LK_RING_BELL ); 1088 return; 1089 1090 default: 1091 if( c >= ' ' && c <= '~' ) { 1092 scanline = qp->scanmap; 1093 b_row = qp->bitmap+(scanline[qp->row*15]&0x3ff)*128+qp->col; 1094 i = c - ' '; 1095 if( i < 0 || i > 95 ) 1096 i = 0; 1097 else 1098 i *= 15; 1099 f_row = (char *)((int)q_font + i); 1100 1101 /* for( i=0 ; i<15 ; i++ , b_row += 128, f_row++ ) 1102 *b_row = *f_row;*/ 1103 /* inline expansion for speed */ 1104 *b_row = *f_row++; b_row += ote; 1105 *b_row = *f_row++; b_row += ote; 1106 *b_row = *f_row++; b_row += ote; 1107 *b_row = *f_row++; b_row += ote; 1108 *b_row = *f_row++; b_row += ote; 1109 *b_row = *f_row++; b_row += ote; 1110 *b_row = *f_row++; b_row += ote; 1111 *b_row = *f_row++; b_row += ote; 1112 *b_row = *f_row++; b_row += ote; 1113 *b_row = *f_row++; b_row += ote; 1114 *b_row = *f_row++; b_row += ote; 1115 *b_row = *f_row++; b_row += ote; 1116 *b_row = *f_row++; b_row += ote; 1117 *b_row = *f_row++; b_row += ote; 1118 *b_row = *f_row++; b_row += ote; 1119 1120 if( ++qp->col >= qp->max_col ) { 1121 qp->col = 0 ; 1122 if( qp->row+1 >= qp->max_row ) 1123 qvscroll(); 1124 else 1125 qp->row++; 1126 } 1127 } 1128 break; 1129 } 1130 } 1131 1132 /* 1133 * Position the cursor to a particular spot. 1134 */ 1135 qv_pos_cur( x, y) 1136 register int x,y; 1137 { 1138 register struct qvdevice *qvaddr; 1139 register struct qv_info *qp = qv_scn; 1140 register index; 1141 1142 if( qvaddr = qp->qvaddr ) { 1143 if( y < 0 || y > qp->max_cur_y ) 1144 y = qp->max_cur_y; 1145 if( x < 0 || x > qp->max_cur_x ) 1146 x = qp->max_cur_x; 1147 qp->cursor.x = x; /* keep track of real cursor*/ 1148 qp->cursor.y = y; /* position, indep. of mouse*/ 1149 1150 qvaddr->qv_crtaddr = 10; /* select cursor start reg */ 1151 qvaddr->qv_crtdata = y & 0xf; 1152 qvaddr->qv_crtaddr = 11; /* select cursor end reg */ 1153 qvaddr->qv_crtdata = y & 0xf; 1154 qvaddr->qv_crtaddr = 14; /* select cursor y pos. */ 1155 qvaddr->qv_crtdata = y >> 4; 1156 qvaddr->qv_xcur = x; /* pos x axis */ 1157 /* 1158 * If the mouse is being used then we change the mode of 1159 * cursor display based on the pixels under the cursor 1160 */ 1161 if( mouseon ) { 1162 index = y*128 + x/8; 1163 if( qp->bitmap[ index ] && qp->bitmap[ index+128 ] ) 1164 qvaddr->qv_csr &= ~QV_CUR_MODE; 1165 else 1166 qvaddr->qv_csr |= QV_CUR_MODE; 1167 } 1168 } 1169 } 1170 /* 1171 * Scroll the bitmap by moving the scanline map words. This could 1172 * be done by moving the bitmap but it's much too slow for a full screen. 1173 * The only drawback is that the scanline map must be reset when the user 1174 * wants to do graphics. 1175 */ 1176 qvscroll() 1177 { 1178 short tmpscanlines[15]; 1179 register char *b_row; 1180 register short *scanline; 1181 register struct qv_info *qp = qv_scn; 1182 1183 /* 1184 * If the mouse is on we don't scroll so that the bit map 1185 * remains sane. 1186 */ 1187 if( mouseon ) { 1188 qp->row = 0; 1189 return; 1190 } 1191 /* 1192 * Save the first 15 scanlines so that we can put them at 1193 * the bottom when done. 1194 */ 1195 bcopy((caddr_t)qp->scanmap, (caddr_t)tmpscanlines, sizeof tmpscanlines); 1196 1197 /* 1198 * Clear the wrapping line so that it won't flash on the bottom 1199 * of the screen. 1200 */ 1201 scanline = qp->scanmap; 1202 b_row = qp->bitmap+(*scanline&0x3ff)*128; 1203 bzero( b_row, 1920 ); 1204 1205 /* 1206 * Now move the scanlines down 1207 */ 1208 bcopy((caddr_t)(qp->scanmap+15), (caddr_t)qp->scanmap, 1209 (qp->row * 15) * sizeof (short) ); 1210 1211 /* 1212 * Now put the other lines back 1213 */ 1214 bcopy((caddr_t)tmpscanlines, (caddr_t)(qp->scanmap+(qp->row * 15)), 1215 sizeof (tmpscanlines) ); 1216 1217 } 1218 1219 /* 1220 * Output to the keyboard. This routine status polls the transmitter on the 1221 * keyboard to output a code. The timer is to avoid hanging on a bad device. 1222 */ 1223 qv_key_out(c) 1224 u_short c; 1225 { 1226 int timer = 30000; 1227 register struct qv_info *qp = qv_scn; 1228 1229 if (qp->qvaddr) { 1230 while ((qp->qvaddr->qv_uartstatus & 0x4) == 0 && timer--) 1231 ; 1232 qp->qvaddr->qv_uartdata = c; 1233 } 1234 } 1235 /* 1236 * Virtual console initialization. This routine sets up the qvss so that it can 1237 * be used as the system console. It is invoked before autoconfig and has to do 1238 * everything necessary to allow the device to serve as the system console. 1239 * In this case it must map the q-bus and device areas and initialize the qvss 1240 * screen. 1241 */ 1242 qvcons_init() 1243 { 1244 struct percpu *pcpu; /* pointer to percpu structure */ 1245 register struct qbus *qb; 1246 struct qvdevice *qvaddr; /* device pointer */ 1247 short *devptr; /* virtual device space */ 1248 extern cnputc(); /* standard serial console putc */ 1249 #define QVSSCSR 017200 1250 1251 /* 1252 * If secondary console already configured, 1253 * don't override the previous one. 1254 */ 1255 if (v_putc != cnputc) 1256 return 0; 1257 /* 1258 * find the percpu entry that matches this machine. 1259 */ 1260 for( pcpu = percpu ; pcpu && pcpu->pc_cputype != cpu ; pcpu++ ) 1261 ; 1262 if( pcpu == NULL ) 1263 return 0; 1264 if (pcpu->pc_io->io_type != IO_QBUS) 1265 return 0; 1266 1267 /* 1268 * Found an entry for this cpu. Because this device is Microvax specific 1269 * we assume that there is a single q-bus and don't have to worry about 1270 * multiple adapters. 1271 * 1272 * Map the device registers. 1273 */ 1274 qb = (struct qbus *)pcpu->pc_io->io_details; 1275 ioaccess(qb->qb_iopage, UMEMmap[0] + qb->qb_memsize, UBAIOPAGES * VAX_NBPG); 1276 1277 /* 1278 * See if the qvss is there. 1279 */ 1280 devptr = (short *)((char *)umem[0] + (qb->qb_memsize * VAX_NBPG)); 1281 qvaddr = (struct qvdevice *)((u_int)devptr + ubdevreg(QVSSCSR)); 1282 if (badaddr((caddr_t)qvaddr, sizeof(short))) 1283 return 0; 1284 /* 1285 * Okay the device is there lets set it up 1286 */ 1287 if (!qv_setup(qvaddr, 0, 0)) 1288 return 0; 1289 v_putc = qvputc; 1290 consops = &qv_cdevsw; 1291 return 1; 1292 } 1293 /* 1294 * Do the board specific setup 1295 */ 1296 qv_setup(qvaddr, unit, probed) 1297 struct qvdevice *qvaddr; 1298 int unit; 1299 int probed; 1300 { 1301 caddr_t qvssmem; /* pointer to the display mem */ 1302 register i; /* simple index */ 1303 register struct qv_info *qp; 1304 register int *pte; 1305 struct percpu *pcpu; /* pointer to percpu structure */ 1306 register struct qbus *qb; 1307 1308 /* 1309 * find the percpu entry that matches this machine. 1310 */ 1311 for( pcpu = percpu ; pcpu && pcpu->pc_cputype != cpu ; pcpu++ ) 1312 ; 1313 if( pcpu == NULL ) 1314 return(0); 1315 1316 /* 1317 * Found an entry for this cpu. Because this device is Microvax specific 1318 * we assume that there is a single q-bus and don't have to worry about 1319 * multiple adapters. 1320 * 1321 * Map the device memory. 1322 */ 1323 qb = (struct qbus *)pcpu->pc_io->io_details; 1324 1325 i = (u_int)(qvaddr->qv_csr & QV_MEM_BANK) << 7; 1326 ioaccess(qb->qb_maddr + i, QVmap[unit], 512 * VAX_NBPG); 1327 qvssmem = qvmem[unit]; 1328 pte = (int *)(QVmap[unit]); 1329 for (i=0; i < 512; i++, pte++) 1330 *pte = (*pte & ~PG_PROT) | PG_UW | PG_V; 1331 1332 qv_scn = (struct qv_info *)((u_int)qvssmem + 251*1024); 1333 qp = qv_scn; 1334 if( (qvaddr->qv_csr & QV_19INCH) && qv_def_scrn == 0) 1335 qv_def_scrn = 1; 1336 *qv_scn = qv_scn_defaults[ qv_def_scrn ]; 1337 if (probed) 1338 qp->qvaddr = qvaddr; 1339 qp->bitmap = qvssmem; 1340 qp->scanmap = (short *)((u_int)qvssmem + 254*1024); 1341 qp->cursorbits = (short *)((u_int)qvssmem + 256*1024-32); 1342 /* set up event queue for later */ 1343 qp->ibuff = (vsEvent *)qp - QVMAXEVQ; 1344 qp->iqsize = QVMAXEVQ; 1345 qp->ihead = qp->itail = 0; 1346 1347 /* 1348 * Setup the crt controller chip. 1349 */ 1350 for( i=0 ; i<16 ; i++ ) { 1351 qvaddr->qv_crtaddr = i; 1352 qvaddr->qv_crtdata = qv_crt_parms[ qv_def_scrn ][ i ]; 1353 } 1354 /* 1355 * Setup the display. 1356 */ 1357 qv_init( qvaddr ); 1358 1359 /* 1360 * Turn on the video 1361 */ 1362 qvaddr->qv_csr |= QV_VIDEO_ENA ; 1363 return 1; 1364 } 1365 #endif 1366