1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 23 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 24 /* All Rights Reserved */ 25 26 /* 27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 /* 34 * PS/2 type Mouse Module - Streams 35 */ 36 37 #include <sys/param.h> 38 #include <sys/types.h> 39 #include <sys/kmem.h> 40 #include <sys/signal.h> 41 #include <sys/errno.h> 42 #include <sys/file.h> 43 #include <sys/termio.h> 44 #include <sys/stream.h> 45 #include <sys/stropts.h> 46 #include <sys/strtty.h> 47 #include <sys/debug.h> 48 #include <sys/ddi.h> 49 #include <sys/stat.h> 50 #include <sys/cmn_err.h> 51 #include <sys/sunddi.h> 52 53 #include <sys/promif.h> 54 #include <sys/cred.h> 55 56 #include <sys/i8042.h> 57 #include <sys/note.h> 58 59 #define DRIVER_NAME(dip) ddi_driver_name(dip) 60 61 #ifdef DEBUG 62 #define MOUSE8042_DEBUG 63 #endif 64 65 #define MOUSE8042_INTERNAL_OPEN(minor) (((minor) & 0x1) == 1) 66 #define MOUSE8042_MINOR_TO_INSTANCE(minor) ((minor) / 2) 67 #define MOUSE8042_INTERNAL_MINOR(minor) ((minor) + 1) 68 69 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t); 70 extern void consconfig_link(major_t major, minor_t minor); 71 extern int consconfig_unlink(major_t major, minor_t minor); 72 73 74 /* 75 * 76 * Local Static Data 77 * 78 */ 79 80 /* 81 * We only support one instance. Yes, it's theoretically possible to 82 * plug in more than one, but it's not worth the implementation cost. 83 * 84 * The introduction of USB keyboards might make it worth reassessing 85 * this decision, as they might free up the keyboard port for a second 86 * PS/2 style mouse. 87 */ 88 static dev_info_t *mouse8042_dip; 89 90 struct mouse_state { 91 queue_t *ms_rqp; 92 queue_t *ms_wqp; 93 ddi_iblock_cookie_t ms_iblock_cookie; 94 ddi_acc_handle_t ms_handle; 95 uint8_t *ms_addr; 96 kmutex_t ms_mutex; 97 98 minor_t ms_minor; 99 boolean_t ms_opened; 100 }; 101 102 #if defined(MOUSE8042_DEBUG) 103 int mouse8042_debug = 0; 104 int mouse8042_debug_minimal = 0; 105 #endif 106 107 static uint_t mouse8042_intr(caddr_t arg); 108 static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag, 109 cred_t *cred_p); 110 static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p); 111 static int mouse8042_wput(queue_t *q, mblk_t *mp); 112 113 static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 114 void *arg, void **result); 115 static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd); 116 static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd); 117 118 119 /* 120 * Streams module info. 121 */ 122 #define MODULE_NAME "mouse8042" 123 124 static struct module_info mouse8042_minfo = { 125 23, /* Module ID number */ 126 MODULE_NAME, 127 0, INFPSZ, /* minimum & maximum packet sizes */ 128 256, 128 /* hi and low water marks */ 129 }; 130 131 static struct qinit mouse8042_rinit = { 132 NULL, /* put */ 133 NULL, /* service */ 134 mouse8042_open, 135 mouse8042_close, 136 NULL, /* admin */ 137 &mouse8042_minfo, 138 NULL /* statistics */ 139 }; 140 141 static struct qinit mouse8042_winit = { 142 mouse8042_wput, /* put */ 143 NULL, /* service */ 144 NULL, /* open */ 145 NULL, /* close */ 146 NULL, /* admin */ 147 &mouse8042_minfo, 148 NULL /* statistics */ 149 }; 150 151 static struct streamtab mouse8042_strinfo = { 152 &mouse8042_rinit, 153 &mouse8042_winit, 154 NULL, /* muxrinit */ 155 NULL, /* muxwinit */ 156 }; 157 158 /* 159 * Local Function Declarations 160 */ 161 162 static struct cb_ops mouse8042_cb_ops = { 163 nodev, /* open */ 164 nodev, /* close */ 165 nodev, /* strategy */ 166 nodev, /* print */ 167 nodev, /* dump */ 168 nodev, /* read */ 169 nodev, /* write */ 170 nodev, /* ioctl */ 171 nodev, /* devmap */ 172 nodev, /* mmap */ 173 nodev, /* segmap */ 174 nochpoll, /* poll */ 175 ddi_prop_op, /* cb_prop_op */ 176 &mouse8042_strinfo, /* streamtab */ 177 D_MP | D_NEW 178 }; 179 180 181 static struct dev_ops mouse8042_ops = { 182 DEVO_REV, /* devo_rev, */ 183 0, /* refcnt */ 184 mouse8042_getinfo, /* getinfo */ 185 nulldev, /* identify */ 186 nulldev, /* probe */ 187 mouse8042_attach, /* attach */ 188 mouse8042_detach, /* detach */ 189 nodev, /* reset */ 190 &mouse8042_cb_ops, /* driver operations */ 191 (struct bus_ops *)0 /* bus operations */ 192 }; 193 194 /* 195 * This is the loadable module wrapper. 196 */ 197 #include <sys/modctl.h> 198 199 extern struct mod_ops mod_driverops; 200 201 /* 202 * Module linkage information for the kernel. 203 */ 204 205 static struct modldrv modldrv = { 206 &mod_driverops, /* Type of module. This one is a driver */ 207 "PS/2 Mouse %I%, %E%", 208 &mouse8042_ops, /* driver ops */ 209 }; 210 211 static struct modlinkage modlinkage = { 212 MODREV_1, 213 (void *)&modldrv, 214 NULL 215 }; 216 217 /* 218 * This is the driver initialization routine. 219 */ 220 int 221 _init() 222 { 223 int rv; 224 225 rv = mod_install(&modlinkage); 226 return (rv); 227 } 228 229 230 int 231 _fini(void) 232 { 233 return (mod_remove(&modlinkage)); 234 } 235 236 237 int 238 _info(struct modinfo *modinfop) 239 { 240 return (mod_info(&modlinkage, modinfop)); 241 } 242 243 static int 244 mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 245 { 246 struct mouse_state *state; 247 mblk_t *mp; 248 int instance = ddi_get_instance(dip); 249 static ddi_device_acc_attr_t attr = { 250 DDI_DEVICE_ATTR_V0, 251 DDI_NEVERSWAP_ACC, 252 DDI_STRICTORDER_ACC, 253 }; 254 int rc; 255 256 257 #ifdef MOUSE8042_DEBUG 258 if (mouse8042_debug) { 259 cmn_err(CE_CONT, MODULE_NAME "_attach entry\n"); 260 } 261 #endif 262 263 if (cmd == DDI_RESUME) { 264 state = (struct mouse_state *)ddi_get_driver_private(dip); 265 266 /* 267 * Send a 0xaa 0x00 upstream. 268 * This causes the vuid module to reset the mouse. 269 */ 270 if (state->ms_rqp != NULL) { 271 if (mp = allocb(1, BPRI_MED)) { 272 *mp->b_wptr++ = 0xaa; 273 putnext(state->ms_rqp, mp); 274 } 275 if (mp = allocb(1, BPRI_MED)) { 276 *mp->b_wptr++ = 0x0; 277 putnext(state->ms_rqp, mp); 278 } 279 } 280 return (DDI_SUCCESS); 281 } 282 283 if (cmd != DDI_ATTACH) 284 return (DDI_FAILURE); 285 286 if (mouse8042_dip != NULL) 287 return (DDI_FAILURE); 288 289 /* allocate and initialize state structure */ 290 state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP); 291 state->ms_opened = B_FALSE; 292 ddi_set_driver_private(dip, state); 293 294 /* 295 * In order to support virtual keyboard/mouse, we should distinguish 296 * between internal virtual open and external physical open. 297 * 298 * When the physical devices are opened by application, they will 299 * be unlinked from the virtual device and their data stream will 300 * not be sent to the virtual device. When the opened physical 301 * devices are closed, they will be relinked to the virtual devices. 302 * 303 * All these automatic switch between virtual and physical are 304 * transparent. 305 * 306 * So we change minor node numbering scheme to be: 307 * external node minor num == instance * 2 308 * internal node minor num == instance * 2 + 1 309 */ 310 rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2, 311 DDI_NT_MOUSE, NULL); 312 if (rc != DDI_SUCCESS) { 313 #if defined(MOUSE8042_DEBUG) 314 cmn_err(CE_CONT, 315 MODULE_NAME "_attach: ddi_create_minor_node failed\n"); 316 #endif 317 goto fail_1; 318 } 319 320 if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR, 321 instance * 2 + 1) != DDI_SUCCESS) { 322 goto fail_2; 323 } 324 325 rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr, 326 (offset_t)0, (offset_t)0, &attr, &state->ms_handle); 327 if (rc != DDI_SUCCESS) { 328 #if defined(MOUSE8042_DEBUG) 329 cmn_err(CE_WARN, MODULE_NAME "_attach: can't map registers"); 330 #endif 331 goto fail_2; 332 } 333 334 rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie); 335 if (rc != DDI_SUCCESS) { 336 #if defined(MOUSE8042_DEBUG) 337 cmn_err(CE_WARN, 338 MODULE_NAME "_attach: Can't get iblock cookie"); 339 #endif 340 goto fail_3; 341 } 342 343 mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER, 344 state->ms_iblock_cookie); 345 346 rc = ddi_add_intr(dip, 0, 347 (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL, 348 mouse8042_intr, (caddr_t)state); 349 if (rc != DDI_SUCCESS) { 350 #if defined(MOUSE8042_DEBUG) 351 cmn_err(CE_WARN, MODULE_NAME "_attach: cannot add interrupt"); 352 #endif 353 goto fail_3; 354 } 355 356 mouse8042_dip = dip; 357 358 /* Now that we're attached, announce our presence to the world. */ 359 ddi_report_dev(dip); 360 #if defined(MOUSE8042_DEBUG) 361 cmn_err(CE_CONT, "?%s #%d: version %s\n", 362 DRIVER_NAME(dip), ddi_get_instance(dip), "%I% (%E%)"); 363 #endif 364 return (DDI_SUCCESS); 365 366 fail_3: 367 ddi_regs_map_free(&state->ms_handle); 368 369 fail_2: 370 ddi_remove_minor_node(dip, NULL); 371 372 fail_1: 373 kmem_free(state, sizeof (struct mouse_state)); 374 return (rc); 375 } 376 377 /*ARGSUSED*/ 378 static int 379 mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 380 { 381 struct mouse_state *state; 382 383 state = ddi_get_driver_private(dip); 384 385 switch (cmd) { 386 case DDI_SUSPEND: 387 return (DDI_SUCCESS); 388 389 case DDI_DETACH: 390 ddi_remove_intr(dip, 0, state->ms_iblock_cookie); 391 mouse8042_dip = NULL; 392 mutex_destroy(&state->ms_mutex); 393 ddi_prop_remove_all(dip); 394 ddi_regs_map_free(&state->ms_handle); 395 ddi_remove_minor_node(dip, NULL); 396 kmem_free(state, sizeof (struct mouse_state)); 397 return (DDI_SUCCESS); 398 399 default: 400 #ifdef MOUSE8042_DEBUG 401 if (mouse8042_debug) { 402 cmn_err(CE_CONT, 403 "mouse8042_detach: cmd = %d unknown\n", cmd); 404 } 405 #endif 406 return (DDI_FAILURE); 407 } 408 } 409 410 411 /* ARGSUSED */ 412 static int 413 mouse8042_getinfo( 414 dev_info_t *dip, 415 ddi_info_cmd_t infocmd, 416 void *arg, 417 void **result) 418 { 419 dev_t dev = (dev_t)arg; 420 minor_t minor = getminor(dev); 421 int instance = MOUSE8042_MINOR_TO_INSTANCE(minor); 422 423 #ifdef MOUSE8042_DEBUG 424 if (mouse8042_debug) 425 cmn_err(CE_CONT, "mouse8042_getinfo: call\n"); 426 #endif 427 switch (infocmd) { 428 case DDI_INFO_DEVT2DEVINFO: 429 if (mouse8042_dip == NULL) 430 return (DDI_FAILURE); 431 432 *result = (void *)mouse8042_dip; 433 break; 434 case DDI_INFO_DEVT2INSTANCE: 435 *result = (void *)(uintptr_t)instance; 436 break; 437 default: 438 return (DDI_FAILURE); 439 } 440 return (DDI_SUCCESS); 441 } 442 443 /*ARGSUSED*/ 444 static int 445 mouse8042_open( 446 queue_t *q, 447 dev_t *devp, 448 int flag, 449 int sflag, 450 cred_t *cred_p) 451 { 452 struct mouse_state *state; 453 minor_t minor = getminor(*devp); 454 int rval; 455 456 if (mouse8042_dip == NULL) 457 return (ENXIO); 458 459 state = ddi_get_driver_private(mouse8042_dip); 460 461 #ifdef MOUSE8042_DEBUG 462 if (mouse8042_debug) 463 cmn_err(CE_CONT, "mouse8042_open:entered\n"); 464 #endif 465 466 mutex_enter(&state->ms_mutex); 467 468 if (state->ms_opened) { 469 /* 470 * Exit if the same minor node is already open 471 */ 472 if (state->ms_minor == minor) { 473 mutex_exit(&state->ms_mutex); 474 return (0); 475 } 476 477 /* 478 * Check whether it is switch between physical and virtual 479 * 480 * Opening from virtual while the device is being physically 481 * opened by an application should not happen. So we ASSERT 482 * this in DEBUG version, and return error in the non-DEBUG 483 * case. 484 */ 485 ASSERT(!MOUSE8042_INTERNAL_OPEN(minor)); 486 487 if (MOUSE8042_INTERNAL_OPEN(minor)) { 488 mutex_exit(&state->ms_mutex); 489 return (EINVAL); 490 } 491 492 /* 493 * Opening the physical one while it is being underneath 494 * the virtual one. 495 * 496 * consconfig_unlink is called to unlink this device from 497 * the virtual one, thus the old stream serving for this 498 * device under the virtual one is closed, and then the 499 * lower driver's close routine (here is mouse8042_close) 500 * is also called to accomplish the whole stream close. 501 * Here we have to drop the lock because mouse8042_close 502 * also needs the lock. 503 * 504 * For mouse, the old stream is: 505 * consms->["pushmod"->]"mouse_vp driver" 506 * 507 * After the consconfig_unlink returns, the old stream is closed 508 * and we grab the lock again to reopen this device as normal. 509 */ 510 mutex_exit(&state->ms_mutex); 511 512 /* 513 * If unlink fails, fail the physical open. 514 */ 515 if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip), 516 MOUSE8042_INTERNAL_MINOR(minor))) != 0) { 517 return (rval); 518 } 519 520 mutex_enter(&state->ms_mutex); 521 } 522 523 524 q->q_ptr = (caddr_t)state; 525 WR(q)->q_ptr = (caddr_t)state; 526 state->ms_rqp = q; 527 state->ms_wqp = WR(q); 528 529 qprocson(q); 530 531 state->ms_minor = minor; 532 state->ms_opened = B_TRUE; 533 534 mutex_exit(&state->ms_mutex); 535 536 return (0); 537 } 538 539 540 /*ARGSUSED*/ 541 static int 542 mouse8042_close(queue_t *q, int flag, cred_t *cred_p) 543 { 544 struct mouse_state *state; 545 minor_t minor; 546 547 state = (struct mouse_state *)q->q_ptr; 548 549 #ifdef MOUSE8042_DEBUG 550 if (mouse8042_debug) 551 cmn_err(CE_CONT, "mouse8042_close:entered\n"); 552 #endif 553 554 mutex_enter(&state->ms_mutex); 555 556 qprocsoff(q); 557 558 q->q_ptr = NULL; 559 WR(q)->q_ptr = NULL; 560 state->ms_rqp = NULL; 561 state->ms_wqp = NULL; 562 563 state->ms_opened = B_FALSE; 564 565 minor = state->ms_minor; 566 567 mutex_exit(&state->ms_mutex); 568 569 if (!MOUSE8042_INTERNAL_OPEN(minor)) { 570 /* 571 * Closing physical PS/2 mouse 572 * 573 * Link it back to virtual mouse, and 574 * mouse8042_open will be called as a result 575 * of the consconfig_link call. Do NOT try 576 * this if the mouse is about to be detached! 577 * 578 * If linking back fails, this specific mouse 579 * will not be available underneath the virtual 580 * mouse, and can only be accessed via physical 581 * open. 582 */ 583 consconfig_link(ddi_driver_major(mouse8042_dip), 584 MOUSE8042_INTERNAL_MINOR(minor)); 585 } 586 587 return (0); 588 } 589 590 static void 591 mouse8042_iocnack( 592 queue_t *qp, 593 mblk_t *mp, 594 struct iocblk *iocp, 595 int error, 596 int rval) 597 { 598 mp->b_datap->db_type = M_IOCNAK; 599 iocp->ioc_rval = rval; 600 iocp->ioc_error = error; 601 qreply(qp, mp); 602 } 603 604 static int 605 mouse8042_wput(queue_t *q, mblk_t *mp) 606 { 607 struct iocblk *iocbp; 608 mblk_t *bp; 609 mblk_t *next; 610 struct mouse_state *state; 611 612 state = (struct mouse_state *)q->q_ptr; 613 614 #ifdef MOUSE8042_DEBUG 615 if (mouse8042_debug) 616 cmn_err(CE_CONT, "mouse8042_wput:entered\n"); 617 #endif 618 iocbp = (struct iocblk *)mp->b_rptr; 619 switch (mp->b_datap->db_type) { 620 case M_FLUSH: 621 #ifdef MOUSE8042_DEBUG 622 if (mouse8042_debug) 623 cmn_err(CE_CONT, "mouse8042_wput:M_FLUSH\n"); 624 #endif 625 626 if (*mp->b_rptr & FLUSHW) 627 flushq(q, FLUSHDATA); 628 qreply(q, mp); 629 break; 630 case M_IOCTL: 631 #ifdef MOUSE8042_DEBUG 632 if (mouse8042_debug) 633 cmn_err(CE_CONT, "mouse8042_wput:M_IOCTL\n"); 634 #endif 635 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 636 break; 637 case M_IOCDATA: 638 #ifdef MOUSE8042_DEBUG 639 if (mouse8042_debug) 640 cmn_err(CE_CONT, "mouse8042_wput:M_IOCDATA\n"); 641 #endif 642 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 643 break; 644 case M_DATA: 645 bp = mp; 646 do { 647 while (bp->b_rptr < bp->b_wptr) { 648 #if defined(MOUSE8042_DEBUG) 649 if (mouse8042_debug) { 650 cmn_err(CE_CONT, 651 "mouse8042: send %2x\n", 652 *bp->b_rptr); 653 } 654 if (mouse8042_debug_minimal) { 655 cmn_err(CE_CONT, ">a:%2x ", 656 *bp->b_rptr); 657 } 658 #endif 659 ddi_put8(state->ms_handle, 660 state->ms_addr + I8042_INT_OUTPUT_DATA, 661 *bp->b_rptr++); 662 } 663 next = bp->b_cont; 664 freeb(bp); 665 } while ((bp = next) != NULL); 666 break; 667 default: 668 freemsg(mp); 669 break; 670 } 671 #ifdef MOUSE8042_DEBUG 672 if (mouse8042_debug) 673 cmn_err(CE_CONT, "mouse8042_wput:leaving\n"); 674 #endif 675 return (0); /* ignored */ 676 } 677 678 static uint_t 679 mouse8042_intr(caddr_t arg) 680 { 681 unsigned char mdata; 682 mblk_t *mp; 683 struct mouse_state *state = (struct mouse_state *)arg; 684 int rc; 685 686 mutex_enter(&state->ms_mutex); 687 688 #if defined(MOUSE8042_DEBUG) 689 if (mouse8042_debug) 690 cmn_err(CE_CONT, "mouse8042_intr()\n"); 691 #endif 692 rc = DDI_INTR_UNCLAIMED; 693 694 for (;;) { 695 696 if (ddi_get8(state->ms_handle, 697 state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) { 698 break; 699 } 700 701 mdata = ddi_get8(state->ms_handle, 702 state->ms_addr + I8042_INT_INPUT_DATA); 703 704 #if defined(MOUSE8042_DEBUG) 705 if (mouse8042_debug) 706 cmn_err(CE_CONT, "mouse8042_intr: got %2x\n", mdata); 707 if (mouse8042_debug_minimal) 708 cmn_err(CE_CONT, "<A:%2x ", mdata); 709 #endif 710 711 rc = DDI_INTR_CLAIMED; 712 713 if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) { 714 *mp->b_wptr++ = mdata; 715 putnext(state->ms_rqp, mp); 716 } 717 } 718 #ifdef MOUSE8042_DEBUG 719 if (mouse8042_debug) 720 cmn_err(CE_CONT, "mouse8042_intr() ok\n"); 721 #endif 722 mutex_exit(&state->ms_mutex); 723 724 return (rc); 725 } 726