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 int instance = ddi_get_instance(dip); 248 static ddi_device_acc_attr_t attr = { 249 DDI_DEVICE_ATTR_V0, 250 DDI_NEVERSWAP_ACC, 251 DDI_STRICTORDER_ACC, 252 }; 253 int rc; 254 255 256 #ifdef MOUSE8042_DEBUG 257 if (mouse8042_debug) { 258 cmn_err(CE_CONT, MODULE_NAME "_attach entry\n"); 259 } 260 #endif 261 262 if (cmd != DDI_ATTACH) 263 return (DDI_FAILURE); 264 265 if (mouse8042_dip != NULL) 266 return (DDI_FAILURE); 267 268 /* allocate and initialize state structure */ 269 state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP); 270 state->ms_opened = B_FALSE; 271 ddi_set_driver_private(dip, state); 272 273 /* 274 * In order to support virtual keyboard/mouse, we should distinguish 275 * between internal virtual open and external physical open. 276 * 277 * When the physical devices are opened by application, they will 278 * be unlinked from the virtual device and their data stream will 279 * not be sent to the virtual device. When the opened physical 280 * devices are closed, they will be relinked to the virtual devices. 281 * 282 * All these automatic switch between virtual and physical are 283 * transparent. 284 * 285 * So we change minor node numbering scheme to be: 286 * external node minor num == instance * 2 287 * internal node minor num == instance * 2 + 1 288 */ 289 rc = ddi_create_minor_node(dip, "l", S_IFCHR, instance * 2, 290 DDI_NT_MOUSE, NULL); 291 if (rc != DDI_SUCCESS) { 292 #if defined(MOUSE8042_DEBUG) 293 cmn_err(CE_CONT, 294 MODULE_NAME "_attach: ddi_create_minor_node failed\n"); 295 #endif 296 goto fail_1; 297 } 298 299 if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR, 300 instance * 2 + 1) != DDI_SUCCESS) { 301 goto fail_2; 302 } 303 304 rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr, 305 (offset_t)0, (offset_t)0, &attr, &state->ms_handle); 306 if (rc != DDI_SUCCESS) { 307 #if defined(MOUSE8042_DEBUG) 308 cmn_err(CE_WARN, MODULE_NAME "_attach: can't map registers"); 309 #endif 310 goto fail_2; 311 } 312 313 rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie); 314 if (rc != DDI_SUCCESS) { 315 #if defined(MOUSE8042_DEBUG) 316 cmn_err(CE_WARN, 317 MODULE_NAME "_attach: Can't get iblock cookie"); 318 #endif 319 goto fail_3; 320 } 321 322 mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER, 323 state->ms_iblock_cookie); 324 325 rc = ddi_add_intr(dip, 0, 326 (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL, 327 mouse8042_intr, (caddr_t)state); 328 if (rc != DDI_SUCCESS) { 329 #if defined(MOUSE8042_DEBUG) 330 cmn_err(CE_WARN, MODULE_NAME "_attach: cannot add interrupt"); 331 #endif 332 goto fail_3; 333 } 334 335 mouse8042_dip = dip; 336 337 /* Now that we're attached, announce our presence to the world. */ 338 ddi_report_dev(dip); 339 #if defined(MOUSE8042_DEBUG) 340 cmn_err(CE_CONT, "?%s #%d: version %s\n", 341 DRIVER_NAME(dip), ddi_get_instance(dip), "%I% (%E%)"); 342 #endif 343 return (DDI_SUCCESS); 344 345 fail_3: 346 ddi_regs_map_free(&state->ms_handle); 347 348 fail_2: 349 ddi_remove_minor_node(dip, NULL); 350 351 fail_1: 352 kmem_free(state, sizeof (struct mouse_state)); 353 return (rc); 354 } 355 356 /*ARGSUSED*/ 357 static int 358 mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 359 { 360 struct mouse_state *state; 361 362 state = ddi_get_driver_private(dip); 363 364 switch (cmd) { 365 366 case DDI_DETACH: 367 ddi_remove_intr(dip, 0, state->ms_iblock_cookie); 368 mouse8042_dip = NULL; 369 mutex_destroy(&state->ms_mutex); 370 ddi_prop_remove_all(dip); 371 ddi_regs_map_free(&state->ms_handle); 372 ddi_remove_minor_node(dip, NULL); 373 kmem_free(state, sizeof (struct mouse_state)); 374 return (DDI_SUCCESS); 375 376 default: 377 #ifdef MOUSE8042_DEBUG 378 if (mouse8042_debug) { 379 cmn_err(CE_CONT, 380 "mouse8042_detach: cmd = %d unknown\n", cmd); 381 } 382 #endif 383 return (DDI_FAILURE); 384 } 385 } 386 387 388 /* ARGSUSED */ 389 static int 390 mouse8042_getinfo( 391 dev_info_t *dip, 392 ddi_info_cmd_t infocmd, 393 void *arg, 394 void **result) 395 { 396 dev_t dev = (dev_t)arg; 397 minor_t minor = getminor(dev); 398 int instance = MOUSE8042_MINOR_TO_INSTANCE(minor); 399 400 #ifdef MOUSE8042_DEBUG 401 if (mouse8042_debug) 402 cmn_err(CE_CONT, "mouse8042_getinfo: call\n"); 403 #endif 404 switch (infocmd) { 405 case DDI_INFO_DEVT2DEVINFO: 406 if (mouse8042_dip == NULL) 407 return (DDI_FAILURE); 408 409 *result = (void *)mouse8042_dip; 410 break; 411 case DDI_INFO_DEVT2INSTANCE: 412 *result = (void *)(uintptr_t)instance; 413 break; 414 default: 415 return (DDI_FAILURE); 416 } 417 return (DDI_SUCCESS); 418 } 419 420 /*ARGSUSED*/ 421 static int 422 mouse8042_open( 423 queue_t *q, 424 dev_t *devp, 425 int flag, 426 int sflag, 427 cred_t *cred_p) 428 { 429 struct mouse_state *state; 430 minor_t minor = getminor(*devp); 431 int rval; 432 433 if (mouse8042_dip == NULL) 434 return (ENXIO); 435 436 state = ddi_get_driver_private(mouse8042_dip); 437 438 #ifdef MOUSE8042_DEBUG 439 if (mouse8042_debug) 440 cmn_err(CE_CONT, "mouse8042_open:entered\n"); 441 #endif 442 443 mutex_enter(&state->ms_mutex); 444 445 if (state->ms_opened) { 446 /* 447 * Exit if the same minor node is already open 448 */ 449 if (state->ms_minor == minor) { 450 mutex_exit(&state->ms_mutex); 451 return (0); 452 } 453 454 /* 455 * Check whether it is switch between physical and virtual 456 * 457 * Opening from virtual while the device is being physically 458 * opened by an application should not happen. So we ASSERT 459 * this in DEBUG version, and return error in the non-DEBUG 460 * case. 461 */ 462 ASSERT(!MOUSE8042_INTERNAL_OPEN(minor)); 463 464 if (MOUSE8042_INTERNAL_OPEN(minor)) { 465 mutex_exit(&state->ms_mutex); 466 return (EINVAL); 467 } 468 469 /* 470 * Opening the physical one while it is being underneath 471 * the virtual one. 472 * 473 * consconfig_unlink is called to unlink this device from 474 * the virtual one, thus the old stream serving for this 475 * device under the virtual one is closed, and then the 476 * lower driver's close routine (here is mouse8042_close) 477 * is also called to accomplish the whole stream close. 478 * Here we have to drop the lock because mouse8042_close 479 * also needs the lock. 480 * 481 * For mouse, the old stream is: 482 * consms->["pushmod"->]"mouse_vp driver" 483 * 484 * After the consconfig_unlink returns, the old stream is closed 485 * and we grab the lock again to reopen this device as normal. 486 */ 487 mutex_exit(&state->ms_mutex); 488 489 /* 490 * If unlink fails, fail the physical open. 491 */ 492 if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip), 493 MOUSE8042_INTERNAL_MINOR(minor))) != 0) { 494 return (rval); 495 } 496 497 mutex_enter(&state->ms_mutex); 498 } 499 500 501 q->q_ptr = (caddr_t)state; 502 WR(q)->q_ptr = (caddr_t)state; 503 state->ms_rqp = q; 504 state->ms_wqp = WR(q); 505 506 qprocson(q); 507 508 state->ms_minor = minor; 509 state->ms_opened = B_TRUE; 510 511 mutex_exit(&state->ms_mutex); 512 513 return (0); 514 } 515 516 517 /*ARGSUSED*/ 518 static int 519 mouse8042_close(queue_t *q, int flag, cred_t *cred_p) 520 { 521 struct mouse_state *state; 522 minor_t minor; 523 524 state = (struct mouse_state *)q->q_ptr; 525 526 #ifdef MOUSE8042_DEBUG 527 if (mouse8042_debug) 528 cmn_err(CE_CONT, "mouse8042_close:entered\n"); 529 #endif 530 531 mutex_enter(&state->ms_mutex); 532 533 qprocsoff(q); 534 535 q->q_ptr = NULL; 536 WR(q)->q_ptr = NULL; 537 state->ms_rqp = NULL; 538 state->ms_wqp = NULL; 539 540 state->ms_opened = B_FALSE; 541 542 minor = state->ms_minor; 543 544 mutex_exit(&state->ms_mutex); 545 546 if (!MOUSE8042_INTERNAL_OPEN(minor)) { 547 /* 548 * Closing physical PS/2 mouse 549 * 550 * Link it back to virtual mouse, and 551 * mouse8042_open will be called as a result 552 * of the consconfig_link call. 553 * 554 * If linking back fails, this specific mouse 555 * will not be available underneath the virtual 556 * mouse, and can only be accessed via physical 557 * open. 558 */ 559 consconfig_link(ddi_driver_major(mouse8042_dip), 560 MOUSE8042_INTERNAL_MINOR(minor)); 561 } 562 563 return (0); 564 } 565 566 static void 567 mouse8042_iocnack( 568 queue_t *qp, 569 mblk_t *mp, 570 struct iocblk *iocp, 571 int error, 572 int rval) 573 { 574 mp->b_datap->db_type = M_IOCNAK; 575 iocp->ioc_rval = rval; 576 iocp->ioc_error = error; 577 qreply(qp, mp); 578 } 579 580 static int 581 mouse8042_wput(queue_t *q, mblk_t *mp) 582 { 583 struct iocblk *iocbp; 584 mblk_t *bp; 585 mblk_t *next; 586 struct mouse_state *state; 587 588 state = (struct mouse_state *)q->q_ptr; 589 590 #ifdef MOUSE8042_DEBUG 591 if (mouse8042_debug) 592 cmn_err(CE_CONT, "mouse8042_wput:entered\n"); 593 #endif 594 iocbp = (struct iocblk *)mp->b_rptr; 595 switch (mp->b_datap->db_type) { 596 case M_FLUSH: 597 #ifdef MOUSE8042_DEBUG 598 if (mouse8042_debug) 599 cmn_err(CE_CONT, "mouse8042_wput:M_FLUSH\n"); 600 #endif 601 602 if (*mp->b_rptr & FLUSHW) 603 flushq(q, FLUSHDATA); 604 qreply(q, mp); 605 break; 606 case M_IOCTL: 607 #ifdef MOUSE8042_DEBUG 608 if (mouse8042_debug) 609 cmn_err(CE_CONT, "mouse8042_wput:M_IOCTL\n"); 610 #endif 611 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 612 break; 613 case M_IOCDATA: 614 #ifdef MOUSE8042_DEBUG 615 if (mouse8042_debug) 616 cmn_err(CE_CONT, "mouse8042_wput:M_IOCDATA\n"); 617 #endif 618 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 619 break; 620 case M_DATA: 621 bp = mp; 622 do { 623 while (bp->b_rptr < bp->b_wptr) { 624 #if defined(MOUSE8042_DEBUG) 625 if (mouse8042_debug) { 626 cmn_err(CE_CONT, 627 "mouse8042: send %2x\n", 628 *bp->b_rptr); 629 } 630 if (mouse8042_debug_minimal) { 631 cmn_err(CE_CONT, ">a:%2x ", 632 *bp->b_rptr); 633 } 634 #endif 635 ddi_put8(state->ms_handle, 636 state->ms_addr + I8042_INT_OUTPUT_DATA, 637 *bp->b_rptr++); 638 } 639 next = bp->b_cont; 640 freeb(bp); 641 } while ((bp = next) != NULL); 642 break; 643 default: 644 freemsg(mp); 645 break; 646 } 647 #ifdef MOUSE8042_DEBUG 648 if (mouse8042_debug) 649 cmn_err(CE_CONT, "mouse8042_wput:leaving\n"); 650 #endif 651 return (0); /* ignored */ 652 } 653 654 static uint_t 655 mouse8042_intr(caddr_t arg) 656 { 657 unsigned char mdata; 658 mblk_t *mp; 659 struct mouse_state *state = (struct mouse_state *)arg; 660 int rc; 661 662 mutex_enter(&state->ms_mutex); 663 664 #if defined(MOUSE8042_DEBUG) 665 if (mouse8042_debug) 666 cmn_err(CE_CONT, "mouse8042_intr()\n"); 667 #endif 668 rc = DDI_INTR_UNCLAIMED; 669 670 for (;;) { 671 672 if (ddi_get8(state->ms_handle, 673 state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) { 674 break; 675 } 676 677 mdata = ddi_get8(state->ms_handle, 678 state->ms_addr + I8042_INT_INPUT_DATA); 679 680 #if defined(MOUSE8042_DEBUG) 681 if (mouse8042_debug) 682 cmn_err(CE_CONT, "mouse8042_intr: got %2x\n", mdata); 683 if (mouse8042_debug_minimal) 684 cmn_err(CE_CONT, "<A:%2x ", mdata); 685 #endif 686 687 rc = DDI_INTR_CLAIMED; 688 689 if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) { 690 *mp->b_wptr++ = mdata; 691 putnext(state->ms_rqp, mp); 692 } 693 } 694 #ifdef MOUSE8042_DEBUG 695 if (mouse8042_debug) 696 cmn_err(CE_CONT, "mouse8042_intr() ok\n"); 697 #endif 698 mutex_exit(&state->ms_mutex); 699 700 return (rc); 701 } 702