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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 23 /* All Rights Reserved */ 24 25 /* 26 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 31 /* 32 * PS/2 type Mouse Module - Streams 33 */ 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 #include <sys/kmem.h> 38 #include <sys/signal.h> 39 #include <sys/errno.h> 40 #include <sys/file.h> 41 #include <sys/termio.h> 42 #include <sys/stream.h> 43 #include <sys/stropts.h> 44 #include <sys/strtty.h> 45 #include <sys/debug.h> 46 #include <sys/ddi.h> 47 #include <sys/stat.h> 48 #include <sys/cmn_err.h> 49 #include <sys/sunddi.h> 50 51 #include <sys/promif.h> 52 #include <sys/cred.h> 53 54 #include <sys/i8042.h> 55 #include <sys/note.h> 56 57 #define DRIVER_NAME(dip) ddi_driver_name(dip) 58 59 #ifdef DEBUG 60 #define MOUSE8042_DEBUG 61 #endif 62 63 #define MOUSE8042_INTERNAL_OPEN(minor) (((minor) & 0x1) == 1) 64 #define MOUSE8042_MINOR_TO_INSTANCE(minor) ((minor) / 2) 65 #define MOUSE8042_INTERNAL_MINOR(minor) ((minor) + 1) 66 67 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t); 68 extern void consconfig_link(major_t major, minor_t minor); 69 extern int consconfig_unlink(major_t major, minor_t minor); 70 71 72 /* 73 * 74 * Local Static Data 75 * 76 */ 77 78 /* 79 * We only support one instance. Yes, it's theoretically possible to 80 * plug in more than one, but it's not worth the implementation cost. 81 * 82 * The introduction of USB keyboards might make it worth reassessing 83 * this decision, as they might free up the keyboard port for a second 84 * PS/2 style mouse. 85 */ 86 static dev_info_t *mouse8042_dip; 87 88 struct mouse_state { 89 queue_t *ms_rqp; 90 queue_t *ms_wqp; 91 ddi_iblock_cookie_t ms_iblock_cookie; 92 ddi_acc_handle_t ms_handle; 93 uint8_t *ms_addr; 94 kmutex_t ms_mutex; 95 96 minor_t ms_minor; 97 boolean_t ms_opened; 98 }; 99 100 #if defined(MOUSE8042_DEBUG) 101 int mouse8042_debug = 0; 102 int mouse8042_debug_minimal = 0; 103 #endif 104 105 static uint_t mouse8042_intr(caddr_t arg); 106 static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag, 107 cred_t *cred_p); 108 static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p); 109 static int mouse8042_wput(queue_t *q, mblk_t *mp); 110 111 static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 112 void *arg, void **result); 113 static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd); 114 static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd); 115 116 117 /* 118 * Streams module info. 119 */ 120 #define MODULE_NAME "mouse8042" 121 122 static struct module_info mouse8042_minfo = { 123 23, /* Module ID number */ 124 MODULE_NAME, 125 0, INFPSZ, /* minimum & maximum packet sizes */ 126 256, 128 /* hi and low water marks */ 127 }; 128 129 static struct qinit mouse8042_rinit = { 130 NULL, /* put */ 131 NULL, /* service */ 132 mouse8042_open, 133 mouse8042_close, 134 NULL, /* admin */ 135 &mouse8042_minfo, 136 NULL /* statistics */ 137 }; 138 139 static struct qinit mouse8042_winit = { 140 mouse8042_wput, /* put */ 141 NULL, /* service */ 142 NULL, /* open */ 143 NULL, /* close */ 144 NULL, /* admin */ 145 &mouse8042_minfo, 146 NULL /* statistics */ 147 }; 148 149 static struct streamtab mouse8042_strinfo = { 150 &mouse8042_rinit, 151 &mouse8042_winit, 152 NULL, /* muxrinit */ 153 NULL, /* muxwinit */ 154 }; 155 156 /* 157 * Local Function Declarations 158 */ 159 160 static struct cb_ops mouse8042_cb_ops = { 161 nodev, /* open */ 162 nodev, /* close */ 163 nodev, /* strategy */ 164 nodev, /* print */ 165 nodev, /* dump */ 166 nodev, /* read */ 167 nodev, /* write */ 168 nodev, /* ioctl */ 169 nodev, /* devmap */ 170 nodev, /* mmap */ 171 nodev, /* segmap */ 172 nochpoll, /* poll */ 173 ddi_prop_op, /* cb_prop_op */ 174 &mouse8042_strinfo, /* streamtab */ 175 D_MP | D_NEW 176 }; 177 178 179 static struct dev_ops mouse8042_ops = { 180 DEVO_REV, /* devo_rev, */ 181 0, /* refcnt */ 182 mouse8042_getinfo, /* getinfo */ 183 nulldev, /* identify */ 184 nulldev, /* probe */ 185 mouse8042_attach, /* attach */ 186 mouse8042_detach, /* detach */ 187 nodev, /* reset */ 188 &mouse8042_cb_ops, /* driver operations */ 189 (struct bus_ops *)0, /* bus operations */ 190 NULL, /* power */ 191 ddi_quiesce_not_needed, /* quiesce */ 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", 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\n", DRIVER_NAME(dip), ddi_get_instance(dip)); 362 #endif 363 return (DDI_SUCCESS); 364 365 fail_3: 366 ddi_regs_map_free(&state->ms_handle); 367 368 fail_2: 369 ddi_remove_minor_node(dip, NULL); 370 371 fail_1: 372 kmem_free(state, sizeof (struct mouse_state)); 373 return (rc); 374 } 375 376 /*ARGSUSED*/ 377 static int 378 mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 379 { 380 struct mouse_state *state; 381 382 state = ddi_get_driver_private(dip); 383 384 switch (cmd) { 385 case DDI_SUSPEND: 386 return (DDI_SUCCESS); 387 388 case DDI_DETACH: 389 ddi_remove_intr(dip, 0, state->ms_iblock_cookie); 390 mouse8042_dip = NULL; 391 mutex_destroy(&state->ms_mutex); 392 ddi_prop_remove_all(dip); 393 ddi_regs_map_free(&state->ms_handle); 394 ddi_remove_minor_node(dip, NULL); 395 kmem_free(state, sizeof (struct mouse_state)); 396 return (DDI_SUCCESS); 397 398 default: 399 #ifdef MOUSE8042_DEBUG 400 if (mouse8042_debug) { 401 cmn_err(CE_CONT, 402 "mouse8042_detach: cmd = %d unknown\n", cmd); 403 } 404 #endif 405 return (DDI_FAILURE); 406 } 407 } 408 409 410 /* ARGSUSED */ 411 static int 412 mouse8042_getinfo( 413 dev_info_t *dip, 414 ddi_info_cmd_t infocmd, 415 void *arg, 416 void **result) 417 { 418 dev_t dev = (dev_t)arg; 419 minor_t minor = getminor(dev); 420 int instance = MOUSE8042_MINOR_TO_INSTANCE(minor); 421 422 #ifdef MOUSE8042_DEBUG 423 if (mouse8042_debug) 424 cmn_err(CE_CONT, "mouse8042_getinfo: call\n"); 425 #endif 426 switch (infocmd) { 427 case DDI_INFO_DEVT2DEVINFO: 428 if (mouse8042_dip == NULL) 429 return (DDI_FAILURE); 430 431 *result = (void *)mouse8042_dip; 432 break; 433 case DDI_INFO_DEVT2INSTANCE: 434 *result = (void *)(uintptr_t)instance; 435 break; 436 default: 437 return (DDI_FAILURE); 438 } 439 return (DDI_SUCCESS); 440 } 441 442 /*ARGSUSED*/ 443 static int 444 mouse8042_open( 445 queue_t *q, 446 dev_t *devp, 447 int flag, 448 int sflag, 449 cred_t *cred_p) 450 { 451 struct mouse_state *state; 452 minor_t minor = getminor(*devp); 453 int rval; 454 455 if (mouse8042_dip == NULL) 456 return (ENXIO); 457 458 state = ddi_get_driver_private(mouse8042_dip); 459 460 #ifdef MOUSE8042_DEBUG 461 if (mouse8042_debug) 462 cmn_err(CE_CONT, "mouse8042_open:entered\n"); 463 #endif 464 465 mutex_enter(&state->ms_mutex); 466 467 if (state->ms_opened) { 468 /* 469 * Exit if the same minor node is already open 470 */ 471 if (state->ms_minor == minor) { 472 mutex_exit(&state->ms_mutex); 473 return (0); 474 } 475 476 /* 477 * Check whether it is switch between physical and virtual 478 * 479 * Opening from virtual while the device is being physically 480 * opened by an application should not happen. So we ASSERT 481 * this in DEBUG version, and return error in the non-DEBUG 482 * case. 483 */ 484 ASSERT(!MOUSE8042_INTERNAL_OPEN(minor)); 485 486 if (MOUSE8042_INTERNAL_OPEN(minor)) { 487 mutex_exit(&state->ms_mutex); 488 return (EINVAL); 489 } 490 491 /* 492 * Opening the physical one while it is being underneath 493 * the virtual one. 494 * 495 * consconfig_unlink is called to unlink this device from 496 * the virtual one, thus the old stream serving for this 497 * device under the virtual one is closed, and then the 498 * lower driver's close routine (here is mouse8042_close) 499 * is also called to accomplish the whole stream close. 500 * Here we have to drop the lock because mouse8042_close 501 * also needs the lock. 502 * 503 * For mouse, the old stream is: 504 * consms->["pushmod"->]"mouse_vp driver" 505 * 506 * After the consconfig_unlink returns, the old stream is closed 507 * and we grab the lock again to reopen this device as normal. 508 */ 509 mutex_exit(&state->ms_mutex); 510 511 /* 512 * If unlink fails, fail the physical open. 513 */ 514 if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip), 515 MOUSE8042_INTERNAL_MINOR(minor))) != 0) { 516 return (rval); 517 } 518 519 mutex_enter(&state->ms_mutex); 520 } 521 522 523 q->q_ptr = (caddr_t)state; 524 WR(q)->q_ptr = (caddr_t)state; 525 state->ms_rqp = q; 526 state->ms_wqp = WR(q); 527 528 qprocson(q); 529 530 state->ms_minor = minor; 531 state->ms_opened = B_TRUE; 532 533 mutex_exit(&state->ms_mutex); 534 535 return (0); 536 } 537 538 539 /*ARGSUSED*/ 540 static int 541 mouse8042_close(queue_t *q, int flag, cred_t *cred_p) 542 { 543 struct mouse_state *state; 544 minor_t minor; 545 546 state = (struct mouse_state *)q->q_ptr; 547 548 #ifdef MOUSE8042_DEBUG 549 if (mouse8042_debug) 550 cmn_err(CE_CONT, "mouse8042_close:entered\n"); 551 #endif 552 553 mutex_enter(&state->ms_mutex); 554 555 qprocsoff(q); 556 557 q->q_ptr = NULL; 558 WR(q)->q_ptr = NULL; 559 state->ms_rqp = NULL; 560 state->ms_wqp = NULL; 561 562 state->ms_opened = B_FALSE; 563 564 minor = state->ms_minor; 565 566 mutex_exit(&state->ms_mutex); 567 568 if (!MOUSE8042_INTERNAL_OPEN(minor)) { 569 /* 570 * Closing physical PS/2 mouse 571 * 572 * Link it back to virtual mouse, and 573 * mouse8042_open will be called as a result 574 * of the consconfig_link call. Do NOT try 575 * this if the mouse is about to be detached! 576 * 577 * If linking back fails, this specific mouse 578 * will not be available underneath the virtual 579 * mouse, and can only be accessed via physical 580 * open. 581 */ 582 consconfig_link(ddi_driver_major(mouse8042_dip), 583 MOUSE8042_INTERNAL_MINOR(minor)); 584 } 585 586 return (0); 587 } 588 589 static void 590 mouse8042_iocnack( 591 queue_t *qp, 592 mblk_t *mp, 593 struct iocblk *iocp, 594 int error, 595 int rval) 596 { 597 mp->b_datap->db_type = M_IOCNAK; 598 iocp->ioc_rval = rval; 599 iocp->ioc_error = error; 600 qreply(qp, mp); 601 } 602 603 static int 604 mouse8042_wput(queue_t *q, mblk_t *mp) 605 { 606 struct iocblk *iocbp; 607 mblk_t *bp; 608 mblk_t *next; 609 struct mouse_state *state; 610 611 state = (struct mouse_state *)q->q_ptr; 612 613 #ifdef MOUSE8042_DEBUG 614 if (mouse8042_debug) 615 cmn_err(CE_CONT, "mouse8042_wput:entered\n"); 616 #endif 617 iocbp = (struct iocblk *)mp->b_rptr; 618 switch (mp->b_datap->db_type) { 619 case M_FLUSH: 620 #ifdef MOUSE8042_DEBUG 621 if (mouse8042_debug) 622 cmn_err(CE_CONT, "mouse8042_wput:M_FLUSH\n"); 623 #endif 624 625 if (*mp->b_rptr & FLUSHW) 626 flushq(q, FLUSHDATA); 627 qreply(q, mp); 628 break; 629 case M_IOCTL: 630 #ifdef MOUSE8042_DEBUG 631 if (mouse8042_debug) 632 cmn_err(CE_CONT, "mouse8042_wput:M_IOCTL\n"); 633 #endif 634 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 635 break; 636 case M_IOCDATA: 637 #ifdef MOUSE8042_DEBUG 638 if (mouse8042_debug) 639 cmn_err(CE_CONT, "mouse8042_wput:M_IOCDATA\n"); 640 #endif 641 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 642 break; 643 case M_DATA: 644 bp = mp; 645 do { 646 while (bp->b_rptr < bp->b_wptr) { 647 #if defined(MOUSE8042_DEBUG) 648 if (mouse8042_debug) { 649 cmn_err(CE_CONT, 650 "mouse8042: send %2x\n", 651 *bp->b_rptr); 652 } 653 if (mouse8042_debug_minimal) { 654 cmn_err(CE_CONT, ">a:%2x ", 655 *bp->b_rptr); 656 } 657 #endif 658 ddi_put8(state->ms_handle, 659 state->ms_addr + I8042_INT_OUTPUT_DATA, 660 *bp->b_rptr++); 661 } 662 next = bp->b_cont; 663 freeb(bp); 664 } while ((bp = next) != NULL); 665 break; 666 default: 667 freemsg(mp); 668 break; 669 } 670 #ifdef MOUSE8042_DEBUG 671 if (mouse8042_debug) 672 cmn_err(CE_CONT, "mouse8042_wput:leaving\n"); 673 #endif 674 return (0); /* ignored */ 675 } 676 677 static uint_t 678 mouse8042_intr(caddr_t arg) 679 { 680 unsigned char mdata; 681 mblk_t *mp; 682 struct mouse_state *state = (struct mouse_state *)arg; 683 int rc; 684 685 mutex_enter(&state->ms_mutex); 686 687 #if defined(MOUSE8042_DEBUG) 688 if (mouse8042_debug) 689 cmn_err(CE_CONT, "mouse8042_intr()\n"); 690 #endif 691 rc = DDI_INTR_UNCLAIMED; 692 693 for (;;) { 694 695 if (ddi_get8(state->ms_handle, 696 state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) { 697 break; 698 } 699 700 mdata = ddi_get8(state->ms_handle, 701 state->ms_addr + I8042_INT_INPUT_DATA); 702 703 #if defined(MOUSE8042_DEBUG) 704 if (mouse8042_debug) 705 cmn_err(CE_CONT, "mouse8042_intr: got %2x\n", mdata); 706 if (mouse8042_debug_minimal) 707 cmn_err(CE_CONT, "<A:%2x ", mdata); 708 #endif 709 710 rc = DDI_INTR_CLAIMED; 711 712 if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) { 713 *mp->b_wptr++ = mdata; 714 putnext(state->ms_rqp, mp); 715 } 716 } 717 #ifdef MOUSE8042_DEBUG 718 if (mouse8042_debug) 719 cmn_err(CE_CONT, "mouse8042_intr() ok\n"); 720 #endif 721 mutex_exit(&state->ms_mutex); 722 723 return (rc); 724 } 725