summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/conskbd.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/uts/common/io/conskbd.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/uts/common/io/conskbd.c')
-rw-r--r--usr/src/uts/common/io/conskbd.c2221
1 files changed, 2221 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/conskbd.c b/usr/src/uts/common/io/conskbd.c
new file mode 100644
index 0000000000..6e0127f2a7
--- /dev/null
+++ b/usr/src/uts/common/io/conskbd.c
@@ -0,0 +1,2221 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Console kbd multiplexor driver for Sun.
+ * The console "zs" port is linked under us, with the "kbd" module pushed
+ * on top of it.
+ * Minor device 0 is what programs normally use.
+ * Minor device 1 is used to feed predigested keystrokes to the "workstation
+ * console" driver, which it is linked beneath.
+ *
+ *
+ * This module can support multiple keyboards to be used simultaneously.
+ * and enable users to use at a time multiple keyboards connected to the
+ * same system. All the keyboards are linked under conskbd, and act as a
+ * keyboard with replicated keys.
+ *
+ * The DIN keyboards of SUN, for exmple , type 3/4/5, are supported via
+ * a two-level architecure. The lower one is one of serialport drivers, such
+ * as zs, se, and the upper is "kb" STREAMS module. Currenly, the serialport
+ * drivers don't support polled I/O interfaces, we couldn't group the keyboard
+ * of this kind under conskbd. So we do as the follows:
+ *
+ * A new ioctl CONSSETKBDTYPE interface between conskbd and lower
+ * keyboard drivers is added. When conskbd receives I_LINK or I_PLINK
+ * ioctl, it will send a CONSSETKBDTYPE ioctl to the driver which is
+ * requesting to be linked under conskbd. If the lower driver does't
+ * recognize this ioctl, the virtual keyboard will be disabled so that
+ * only one keyboard instance could be linked under conskbd.
+ */
+#define KEYMAP_SIZE_VARIABLE
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strsubr.h>
+#include <sys/strsun.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/modctl.h>
+#include <sys/kbio.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/consdev.h>
+#include <sys/note.h>
+#include <sys/kmem.h>
+#include <sys/kstat.h>
+#include <sys/policy.h>
+#include <sys/kbd.h>
+#include <sys/kbtrans.h>
+#include <sys/promif.h>
+#include <sys/vuid_event.h>
+#include <sys/conskbd.h>
+
+extern struct keyboard *kbtrans_usbkb_maptab_init(void);
+extern void kbtrans_usbkb_maptab_fini(struct keyboard **);
+extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
+
+/*
+ * Module linkage routines for the kernel
+ */
+static int conskbd_attach(dev_info_t *, ddi_attach_cmd_t);
+static int conskbd_detach(dev_info_t *, ddi_detach_cmd_t);
+static int conskbd_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
+
+/*
+ * STREAMS queue processing procedures
+ */
+static void conskbduwsrv(queue_t *);
+static void conskbdlwserv(queue_t *);
+static void conskbdlrput(queue_t *, mblk_t *);
+static void conskbdioctl(queue_t *, mblk_t *);
+static int conskbdclose(queue_t *, int, cred_t *);
+static int conskbdopen(queue_t *, dev_t *, int, int, cred_t *);
+
+
+/* STREAMS driver id and limit value struct */
+static struct module_info conskbdm_info = {
+ 0, /* mi_idnum */
+ "conskbd", /* mi_idname */
+ 0, /* mi_minpsz */
+ 1024, /* mi_maxpsz */
+ 2048, /* mi_hiwat */
+ 128 /* mi_lowat */
+};
+
+/*
+ * STREAMS queue processing procedure structures
+ */
+/* upper read queue processing procedure structures */
+static struct qinit conskbdurinit = {
+ NULL, /* qi_putp */
+ (int (*)())NULL, /* qi_srvp */
+ conskbdopen, /* qi_qopen */
+ conskbdclose, /* qi_qclose */
+ (int (*)())NULL, /* qi_qadmin */
+ &conskbdm_info, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+/* upper write queue processing procedures structuresi */
+static struct qinit conskbduwinit = {
+ (int (*)())putq, /* qi_putp */
+ (int (*)())conskbduwsrv, /* qi_srvp */
+ conskbdopen, /* qi_qopen */
+ conskbdclose, /* qi_qclose */
+ (int (*)())NULL, /* qi_qadmin */
+ &conskbdm_info, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+/* lower read queue processing procedures structures */
+static struct qinit conskbdlrinit = {
+ (int (*)())conskbdlrput, /* qi_putp */
+ (int (*)())NULL, /* qi_srvp */
+ (int (*)())NULL, /* qi_qopen */
+ (int (*)())NULL, /* qi_qclose */
+ (int (*)())NULL, /* qi_qadmin */
+ &conskbdm_info, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+/* lower write processing procedures structures */
+static struct qinit conskbdlwinit = {
+ putq, /* qi_putp */
+ (int (*)())conskbdlwserv, /* qi_srvp */
+ (int (*)())NULL, /* qi_qopen */
+ (int (*)())NULL, /* qi_qclose */
+ (int (*)())NULL, /* qi_qadmin */
+ &conskbdm_info, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+/* STREAMS entity declaration structure */
+static struct streamtab conskbd_str_info = {
+ &conskbdurinit, /* st_rdinit */
+ &conskbduwinit, /* st_wrinit */
+ &conskbdlrinit, /* st_muxrinit */
+ &conskbdlwinit, /* st_muxwinit */
+};
+
+
+/* Entry points structure */
+static struct cb_ops cb_conskbd_ops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ &conskbd_str_info, /* cb_stream */
+ D_MP | D_MTOUTPERIM /* cb_flag */
+};
+
+
+/*
+ * Device operations structure
+ */
+static struct dev_ops conskbd_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ conskbd_info, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ conskbd_attach, /* devo_attach */
+ conskbd_detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &(cb_conskbd_ops), /* devo_cb_ops */
+ (struct bus_ops *)NULL, /* devo_bus_ops */
+ NULL /* devo_power */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a pseudo driver */
+ "Console kbd Multiplexer driver 'conskbd' %I%",
+ &conskbd_ops, /* driver ops */
+};
+
+/*
+ * Module linkage structure
+ */
+static struct modlinkage modlinkage = {
+ MODREV_1, /* ml_rev */
+ &modldrv, /* ml_linkage */
+ NULL /* NULL terminates the list */
+};
+
+/*
+ * Debug printing
+ */
+#ifndef DPRINTF
+#ifdef DEBUG
+void conskbd_dprintf(const char *fmt, ...);
+#define DPRINTF(l, m, args) \
+ (((l) >= conskbd_errlevel) && ((m) & conskbd_errmask) ? \
+ conskbd_dprintf args : \
+ (void) 0)
+
+/*
+ * Severity levels for printing
+ */
+#define PRINT_L0 0 /* print every message */
+#define PRINT_L1 1 /* debug */
+#define PRINT_L2 2 /* quiet */
+
+/*
+ * Masks
+ */
+#define PRINT_MASK_ALL 0xFFFFFFFFU
+uint_t conskbd_errmask = PRINT_MASK_ALL;
+uint_t conskbd_errlevel = PRINT_L2;
+
+#else
+#define DPRINTF(l, m, args) /* NOTHING */
+#endif
+#endif
+
+/*
+ * Module global data are protected by the per-module inner perimeter
+ */
+static queue_t *conskbd_regqueue; /* regular keyboard queue above us */
+static queue_t *conskbd_consqueue; /* console queue above us */
+
+
+static dev_info_t *conskbd_dip; /* private copy of devinfo pointer */
+static long conskbd_idle_stamp; /* seconds tstamp of latest keystroke */
+static struct keyboard *conskbd_keyindex;
+
+/*
+ * Normally, kstats of type KSTAT_TYPE_NAMED have multiple elements. In
+ * this case we use this type for a single element because the ioctl code
+ * for it knows how to handle mixed kernel/user data models. Also, it
+ * will be easier to add new statistics later.
+ */
+static struct {
+ kstat_named_t idle_sec; /* seconds since last keystroke */
+} conskbd_kstat = {
+ { "idle_sec", KSTAT_DATA_LONG, }
+};
+
+/*
+ * Local routines prototypes
+ */
+static int conskbd_kstat_update(kstat_t *, int);
+
+static void conskbd_ioc_plink(queue_t *, mblk_t *);
+static void conskbd_legacy_kbd_ioctl(queue_t *, mblk_t *);
+static void conskbd_virtual_kbd_ioctl(queue_t *, mblk_t *);
+
+static conskbd_pending_msg_t *conskbd_mux_find_msg(mblk_t *);
+static void conskbd_mux_enqueue_msg(conskbd_pending_msg_t *);
+static void conskbd_mux_dequeue_msg(conskbd_pending_msg_t *);
+static void conskbd_link_lower_queue(conskbd_lower_queue_t *);
+
+static void conskbd_handle_downstream_msg(queue_t *, mblk_t *);
+static void conskbd_kioctype_complete(conskbd_lower_queue_t *, mblk_t *);
+static void conskbd_kioctrans_complete(conskbd_lower_queue_t *, mblk_t *);
+static void conskbd_kioclayout_complete(conskbd_lower_queue_t *, mblk_t *);
+static void conskbd_kiocsled_complete(conskbd_lower_queue_t *, mblk_t *);
+static void conskbd_mux_upstream_msg(conskbd_lower_queue_t *, mblk_t *);
+static void conskbd_legacy_upstream_msg(conskbd_lower_queue_t *, mblk_t *);
+static void conskbd_lqs_ack_complete(conskbd_lower_queue_t *, mblk_t *);
+
+static void conskbd_polledio_enter(struct cons_polledio_arg *);
+static void conskbd_polledio_exit(struct cons_polledio_arg *);
+static int conskbd_polledio_ischar(struct cons_polledio_arg *);
+static int conskbd_polledio_getchar(struct cons_polledio_arg *);
+static void conskbd_polledio_setled(struct kbtrans_hardware *, int);
+
+static void conskbd_streams_setled(struct kbtrans_hardware *, int);
+static boolean_t conskbd_override_kbtrans(queue_t *, mblk_t *);
+static boolean_t
+conskbd_polled_keycheck(struct kbtrans_hardware *,
+ kbtrans_key_t *, enum keystate *);
+
+/*
+ * Callbacks needed by kbtrans
+ */
+static struct kbtrans_callbacks conskbd_callbacks = {
+ conskbd_streams_setled,
+ conskbd_polledio_setled,
+ conskbd_polled_keycheck,
+};
+
+/*
+ * Single private "global" lock for the few rare conditions
+ * we want single-threaded.
+ */
+static kmutex_t conskbd_lq_lock;
+static kmutex_t conskbd_msgq_lock;
+static conskbd_pending_msg_t *conskbd_msg_queue;
+
+/*
+ * The software state structure of virtual keyboard.
+ * Currently, only one virtual keyboard is support.
+ */
+static conskbd_state_t conskbd = { 0 };
+
+/*
+ * _init()
+ *
+ * Description:
+ * Driver initialization, called when driver is first loaded.
+ * This is how access is initially given to all the static structures.
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
+ * mod_install() status, see mod_install(9f)
+ */
+int
+_init(void)
+{
+ int error;
+
+ error = mod_install(&modlinkage);
+ if (error != 0) {
+ return (error);
+ }
+
+ conskbd_keyindex = kbtrans_usbkb_maptab_init();
+
+ mutex_init(&conskbd_lq_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&conskbd_msgq_lock, NULL, MUTEX_DRIVER, NULL);
+
+ return (error);
+
+} /* _init() */
+
+/*
+ * _fini()
+ *
+ * Description:
+ * Module de-initialization, called when the driver is to be unloaded.
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * mod_remove() status, see mod_remove(9f)
+ */
+int
+_fini(void)
+{
+ int error;
+
+ error = mod_remove(&modlinkage);
+ if (error != 0)
+ return (error);
+ mutex_destroy(&conskbd_lq_lock);
+ mutex_destroy(&conskbd_msgq_lock);
+ kbtrans_usbkb_maptab_fini(&conskbd_keyindex);
+
+ return (0);
+
+} /* _fini() */
+
+/*
+ * _info()
+ *
+ * Description:
+ * Module information, returns information about the driver.
+ *
+ * Arguments:
+ * modinfo *modinfop Pointer to the opaque modinfo structure
+ *
+ * Returns:
+ * mod_info() status, see mod_info(9f)
+ */
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+
+} /* _info() */
+
+
+/*
+ * conskbd_attach()
+ *
+ * Description:
+ * This routine creates two device nodes. One is the "kbd" node, which
+ * is used by user application programs(such as Xserver).The other is the
+ * "conskbd" node, which is an internal node. consconfig_dacf module will
+ * open this internal node, and link the conskbd under the wc (workstaion
+ * console).
+ *
+ * Arguments:
+ * dev_info_t *dip Pointer to the device's dev_info struct
+ * ddi_attach_cmd_t cmd Attach command
+ *
+ * Returns:
+ * DDI_SUCCESS The driver was initialized properly
+ * DDI_FAILURE The driver couldn't be initialized properly
+ */
+/*ARGSUSED*/
+static int
+conskbd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ kstat_t *ksp;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+
+ default:
+ return (DDI_FAILURE);
+
+ }
+ if ((ddi_create_minor_node(devi, "kbd", S_IFCHR,
+ 0, DDI_PSEUDO, NULL) == DDI_FAILURE) ||
+ (ddi_create_internal_pathname(devi, "conskbd", S_IFCHR,
+ 1) == DDI_FAILURE)) {
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+ conskbd_dip = devi;
+
+ ksp = kstat_create("conskbd", 0, "activity", "misc", KSTAT_TYPE_NAMED,
+ sizeof (conskbd_kstat) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL);
+ if (ksp) {
+ ksp->ks_data = (void *) &conskbd_kstat;
+ ksp->ks_update = conskbd_kstat_update;
+ kstat_install(ksp);
+ conskbd_idle_stamp = gethrestime_sec(); /* initial value */
+ }
+
+ conskbd.conskbd_layout = -1; /* invalid layout */
+ conskbd.conskbd_led_state = -1;
+ conskbd.conskbd_bypassed = B_FALSE;
+
+ return (DDI_SUCCESS);
+
+} /* conskbd_attach() */
+
+/*
+ * conskbd_detach()
+ *
+ * Description:
+ * Detach an instance of the conskbd driver. In fact, the driver can not
+ * be detached.
+ *
+ * Arguments:
+ * dev_info_t *dip Pointer to the device's dev_info struct
+ * ddi_detach_cmd_t cmd Detach command
+ *
+ * Returns:
+ * DDI_SUCCESS The driver was detached
+ * DDI_FAILURE The driver couldn't be detached
+ */
+/*ARGSUSED*/
+static int
+conskbd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ return (DDI_FAILURE);
+
+} /* conskbd_detach() */
+
+/* ARGSUSED */
+static int
+conskbd_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **result)
+{
+ register int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (conskbd_dip == NULL) {
+ error = DDI_FAILURE;
+ } else {
+ *result = (void *) conskbd_dip;
+ error = DDI_SUCCESS;
+ }
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+
+} /* conskbd_info() */
+
+/*ARGSUSED*/
+static int
+conskbdopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
+{
+ dev_t unit;
+ int err;
+
+ unit = getminor(*devp);
+
+ if (unit == 0) {
+ /*
+ * Opening "/dev/kbd".
+ */
+ conskbd_regqueue = q;
+ qprocson(q);
+ return (0);
+ } else if (unit != 1) {
+ /* we don't do that under Bozo's Big Tent */
+ return (ENODEV);
+ }
+
+ /*
+ * Opening the device to be linked under the console.
+ */
+ conskbd_consqueue = q;
+
+ /*
+ * initialzie kbtrans module for conskbd
+ */
+ err = kbtrans_streams_init(q, sflag, crp, (struct kbtrans_hardware *)
+ &conskbd, &conskbd_callbacks, &conskbd.conskbd_kbtrans, 0, 0);
+ if (err != 0)
+ return (err);
+ kbtrans_streams_set_keyboard(conskbd.conskbd_kbtrans, KB_USB,
+ conskbd_keyindex);
+
+ conskbd.conskbd_polledio.cons_polledio_version = CONSPOLLEDIO_V1;
+ conskbd.conskbd_polledio.cons_polledio_argument =
+ (struct cons_polledio_arg *)&conskbd;
+ conskbd.conskbd_polledio.cons_polledio_putchar = NULL;
+ conskbd.conskbd_polledio.cons_polledio_getchar =
+ (int (*)(struct cons_polledio_arg *)) conskbd_polledio_getchar;
+ conskbd.conskbd_polledio.cons_polledio_ischar =
+ (boolean_t (*)(struct cons_polledio_arg *))conskbd_polledio_ischar;
+ conskbd.conskbd_polledio.cons_polledio_enter = conskbd_polledio_enter;
+ conskbd.conskbd_polledio.cons_polledio_exit = conskbd_polledio_exit;
+ qprocson(q);
+ kbtrans_streams_enable(conskbd.conskbd_kbtrans);
+
+ return (0);
+
+} /* conskbd_open() */
+
+
+/*ARGSUSED*/
+static int
+conskbdclose(queue_t *q, int flag, cred_t *crp)
+{
+ if (q == conskbd_regqueue) {
+
+ /* switch the input stream back to conskbd_consqueue */
+ conskbd.conskbd_directio = B_FALSE;
+
+ kbtrans_streams_untimeout(conskbd.conskbd_kbtrans);
+ kbtrans_streams_set_queue(conskbd.conskbd_kbtrans,
+ conskbd_consqueue);
+ qprocsoff(q);
+ conskbd_regqueue = NULL;
+ } else if (q == conskbd_consqueue) {
+ /*
+ * Well, this is probably a mistake, but we will permit you
+ * to close the path to the console if you really insist.
+ */
+ qprocsoff(q);
+ conskbd_consqueue = NULL;
+ }
+
+ return (0);
+
+} /* conskbd_close() */
+
+/*
+ * Service procedure for upper write queue.
+ * To make sure the order of messages, we don't process any
+ * message in qi_putq() routine of upper write queue, instead the
+ * qi_putq() routine, which is a standard putq() routine, puts all
+ * messages into a queue, and lets the following service procedure
+ * deal with all messages.
+ * This routine is invoked when ioctl commands are send down
+ * by a consumer of the keyboard device, eg, when the keyboard
+ * consumer tries to determine the keyboard layout type, or sets
+ * the led states.
+ */
+static void
+conskbduwsrv(queue_t *q)
+{
+ mblk_t *mp;
+ queue_t *oldq;
+ enum kbtrans_message_response ret;
+
+ while ((mp = getq(q)) != NULL) {
+
+ /*
+ * if the virtual keyboard is supported
+ */
+ if (conskbd.conskbd_bypassed == B_FALSE) {
+
+ if (conskbd_override_kbtrans(q, mp) == B_TRUE)
+ continue;
+ /*
+ * The conskbd driver is a psaudo driver. It has two
+ * devcice nodes, one is used by kernel, and the other
+ * is used by end-users. There are two STREAMS queues
+ * corresponding to the two device nodes, console queue
+ * and regular queue.
+ * In conskbd_override_kbtrans() routine, when receives
+ * KIOCSDIRECT ioctl, we need change the direction of
+ * keyboard input messages, and direct the input stream
+ * from keyboard into right queue. It causes this queue
+ * to be switched between regular queue and console
+ * queue. And here, in this routine, the in-parameter
+ * "q" can be any one of the two. Moreover, this module
+ * is executed in multithreaded environment, even if the
+ * q is switched to regular queue, it is possible that
+ * the in-parameter is still the console queue, and we
+ * need to return response to right queue.
+ * The response is sent to upstream by the kbtrans
+ * module. so we need to save the old queue, and wait
+ * kbtrans to proces message and to send response out,
+ * and then switch back to old queue.
+ */
+ oldq = kbtrans_streams_get_queue(
+ conskbd.conskbd_kbtrans);
+ kbtrans_streams_set_queue(
+ conskbd.conskbd_kbtrans, RD(q));
+ ret = kbtrans_streams_message(
+ conskbd.conskbd_kbtrans, mp);
+ kbtrans_streams_set_queue(
+ conskbd.conskbd_kbtrans, oldq);
+
+ switch (ret) {
+ case KBTRANS_MESSAGE_HANDLED:
+ continue;
+ case KBTRANS_MESSAGE_NOT_HANDLED:
+ break;
+ }
+ }
+
+ switch (mp->b_datap->db_type) {
+
+ case M_IOCTL:
+ conskbdioctl(q, mp);
+ break;
+
+ case M_FLUSH:
+ if (*mp->b_rptr & FLUSHW) {
+ flushq(q, FLUSHDATA);
+ }
+ /*
+ * here, if flush read queue, some key-up messages
+ * may be lost so that upper module or applications
+ * treat corresponding keys as being held down for
+ * ever.
+ */
+ freemsg(mp);
+ break;
+
+ case M_DATA:
+ /*
+ * virtual keyboard doesn't support this interface.
+ * only when it is disabled, we pass the message
+ * down to lower queue.
+ */
+ if ((conskbd.conskbd_bypassed) &&
+ (conskbd.conskbd_lqueue_nums > 0)) {
+ if (putq(conskbd.conskbd_lqueue_list->
+ lqs_queue, mp) != 1)
+ freemsg(mp);
+ } else {
+ freemsg(mp);
+ }
+ break;
+
+ default:
+ /*
+ * Pass an error message up.
+ */
+ mp->b_datap->db_type = M_ERROR;
+ if (mp->b_cont) {
+ freemsg(mp->b_cont);
+ mp->b_cont = NULL;
+ }
+ mp->b_rptr = mp->b_datap->db_base;
+ mp->b_wptr = mp->b_rptr + sizeof (char);
+ *mp->b_rptr = EINVAL;
+ qreply(q, mp);
+ }
+ } /* end of while */
+
+} /* conskbduwsrv() */
+
+static void
+conskbdioctl(queue_t *q, mblk_t *mp)
+{
+ conskbd_lower_queue_t *prev;
+ conskbd_lower_queue_t *lqs;
+ struct iocblk *iocp;
+ struct linkblk *linkp;
+ int index;
+ int error = 0;
+
+ iocp = (struct iocblk *)mp->b_rptr;
+
+ switch (iocp->ioc_cmd) {
+
+ case I_LINK:
+ case I_PLINK:
+ mutex_enter(&conskbd_lq_lock);
+ conskbd_ioc_plink(q, mp);
+ mutex_exit(&conskbd_lq_lock);
+ break;
+
+ case I_UNLINK:
+ case I_PUNLINK:
+ mutex_enter(&conskbd_lq_lock);
+ linkp = (struct linkblk *)mp->b_cont->b_rptr;
+ prev = conskbd.conskbd_lqueue_list;
+ for (lqs = prev; lqs; lqs = lqs->lqs_next) {
+ if (lqs->lqs_queue == linkp->l_qbot) {
+ if (prev == lqs)
+ conskbd.conskbd_lqueue_list =
+ lqs->lqs_next;
+ else
+ prev->lqs_next = lqs->lqs_next;
+
+ lqs->lqs_queue->q_ptr = NULL;
+ conskbd.conskbd_lqueue_nums --;
+ if (conskbd.conskbd_lqueue_nums == 0)
+ conskbd.conskbd_layout = -1;
+
+ mutex_exit(&conskbd_lq_lock);
+
+ for (index = 0; index < KBTRANS_KEYNUMS_MAX;
+ index ++) {
+ if (lqs->lqs_key_state[index] ==
+ KEY_PRESSED)
+ kbtrans_streams_key(
+ conskbd.conskbd_kbtrans,
+ index,
+ KEY_RELEASED);
+ }
+
+ kmem_free(lqs, sizeof (*lqs));
+ miocack(q, mp, 0, 0);
+ return;
+ }
+ prev = lqs;
+ }
+ mutex_exit(&conskbd_lq_lock);
+ miocnak(q, mp, 0, EINVAL);
+ break;
+
+ case KIOCSKABORTEN:
+ /*
+ * Check if privileged
+ */
+ if ((error = secpolicy_sys_config(iocp->ioc_cr, B_FALSE))) {
+ miocnak(q, mp, 0, error);
+ return;
+ }
+
+ error = miocpullup(mp, sizeof (int));
+ if (error != 0) {
+ miocnak(q, mp, 0, error);
+ return;
+ }
+
+ abort_enable = *(int *)mp->b_cont->b_rptr;
+ miocack(q, mp, 0, 0);
+ break;
+
+ default:
+ if (conskbd.conskbd_bypassed == B_TRUE) {
+ conskbd_legacy_kbd_ioctl(q, mp);
+ } else {
+ conskbd_virtual_kbd_ioctl(q, mp);
+ }
+ }
+
+} /* conskbdioctl() */
+
+
+static void
+conskbd_virtual_kbd_ioctl(queue_t *q, mblk_t *mp)
+{
+ struct iocblk *iocp;
+ mblk_t *datap;
+ int cmd;
+ int error = 0;
+
+ iocp = (struct iocblk *)mp->b_rptr;
+
+ switch (iocp->ioc_cmd) {
+ case KIOCLAYOUT:
+ if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
+ miocnak(q, mp, 0, ENOMEM);
+ break;
+ }
+
+ if (conskbd.conskbd_layout == -1)
+ *(int *)datap->b_wptr = KBTRANS_USBKB_DEFAULT_LAYOUT;
+ else
+ *(int *)datap->b_wptr = conskbd.conskbd_layout;
+
+ datap->b_wptr += sizeof (int);
+ if (mp->b_cont)
+ freemsg(mp->b_cont);
+ mp->b_cont = datap;
+ miocack(q, mp, sizeof (int), 0);
+ break;
+
+ case KIOCSLAYOUT:
+ if (iocp->ioc_count != TRANSPARENT) {
+ miocnak(q, mp, 0, EINVAL);
+ break;
+ }
+ conskbd.conskbd_layout = *(intptr_t *)(mp->b_cont->b_rptr);
+ miocack(q, mp, 0, 0);
+ break;
+
+ case CONSOPENPOLLEDIO:
+ error = miocpullup(mp, sizeof (struct cons_polledio *));
+ if (error != 0) {
+ miocnak(q, mp, 0, error);
+ break;
+ }
+ if (conskbd.conskbd_lqueue_list == NULL) {
+ miocnak(q, mp, 0, EINVAL);
+ break;
+ }
+ conskbd_handle_downstream_msg(q, mp);
+ break;
+
+ case CONSCLOSEPOLLEDIO:
+ if (conskbd.conskbd_lqueue_list == NULL) {
+ miocnak(q, mp, 0, EINVAL);
+ break;
+ }
+ conskbd_handle_downstream_msg(q, mp);
+ break;
+
+ case CONSSETABORTENABLE:
+ /*
+ * To enable combined STOP-A(or F1-A) to trap into kmdb,
+ * the lower physical keyboard drivers are always told not
+ * to parse abort sequence(refer to consconfig_dacf module).
+ * Instead, lower drivers always send all keydown & keyup
+ * messages up to conskbd, so that when key STOP(or F1) is
+ * pressed on one keyboard and key A is pressed on another
+ * keyboard, the system could trap into kmdb.
+ *
+ * When we by kbtrans_streams_message() invoked kbtrans to
+ * handle ioctls in conskbduwsrv() routine, kbtrans module
+ * already handle the message though it returned to us a
+ * KBTRANS_MESSAGE_NOT_HANDLED. For virtual keyboard, no
+ * special initialization or un-initialization is needed.
+ * So we just return ACK to upper module.
+ */
+ miocack(q, mp, 0, 0);
+ break;
+
+ case KIOCCMD:
+ if (conskbd.conskbd_lqueue_list == NULL ||
+ mp->b_cont == NULL) {
+ miocnak(q, mp, 0, EINVAL);
+ break;
+ }
+ cmd = *(int *)mp->b_cont->b_rptr;
+ if (cmd == KBD_CMD_GETLAYOUT) {
+ freemsg(mp->b_cont);
+ datap = allocb(sizeof (int), BPRI_HI);
+ if (datap == NULL) {
+ miocnak(q, mp, 0, ENOMEM);
+ return;
+ }
+ if (conskbd.conskbd_layout == -1)
+ *(int *)datap->b_wptr =
+ KBTRANS_USBKB_DEFAULT_LAYOUT;
+ else
+ *(int *)datap->b_wptr = conskbd.conskbd_layout;
+
+ mp->b_cont = datap;
+ miocack(q, mp, sizeof (int), 0);
+ return;
+ }
+
+ conskbd_handle_downstream_msg(q, mp);
+ break;
+
+ default:
+ miocnak(q, mp, 0, EINVAL);
+ break;
+ }
+
+} /* conskbd_virtual_kbd_ioctl() */
+
+static void
+conskbd_legacy_kbd_ioctl(queue_t *q, mblk_t *mp)
+{
+ conskbd_lower_queue_t *lq;
+ struct iocblk *iocp;
+ int error = 0;
+
+ iocp = (struct iocblk *)mp->b_rptr;
+
+ ASSERT(conskbd.conskbd_lqueue_nums == 1);
+ switch (iocp->ioc_cmd) {
+
+ case KIOCGDIRECT: {
+ mblk_t *datap;
+
+ if ((datap = allocb(sizeof (int), BPRI_MED)) == NULL) {
+ miocnak(q, mp, 0, ENOMEM);
+ break;
+ }
+
+ *(int *)datap->b_wptr = conskbd.conskbd_directio;
+ datap->b_wptr += sizeof (int);
+ if (mp->b_cont != NULL) {
+ freemsg(mp->b_cont);
+ mp->b_cont = NULL;
+ }
+ mp->b_cont = datap;
+ miocack(q, mp, sizeof (int), 0);
+ break;
+ }
+
+ case KIOCSDIRECT:
+ error = miocpullup(mp, sizeof (int));
+ if (error != 0) {
+ miocnak(q, mp, 0, error);
+ break;
+ }
+ conskbd.conskbd_directio = *(int *)mp->b_cont->b_rptr;
+
+ /*
+ * Pass this through, if there's something to pass
+ * it through to, so the system keyboard can reset
+ * itself.
+ */
+ if (conskbd.conskbd_lqueue_nums > 0) {
+ lq = conskbd.conskbd_lqueue_list;
+ ASSERT(lq && lq->lqs_next == NULL);
+ if (putq(lq->lqs_queue, mp) != 1) {
+ miocnak(q, mp, 0, ENOMEM);
+ return;
+ }
+ break;
+ }
+
+ miocack(q, mp, 0, 0);
+ break;
+
+ default:
+ /*
+ * Pass this through, if there's something to pass it
+ * through to; otherwise, reject it.
+ */
+ if (conskbd.conskbd_lqueue_nums > 0) {
+ lq = conskbd.conskbd_lqueue_list;
+ ASSERT(lq && lq->lqs_next == NULL);
+ if (putq(lq->lqs_queue, mp) != 1) {
+ miocnak(q, mp, 0, ENOMEM);
+ return;
+ }
+ break;
+ }
+
+ /* nobody below us; reject it */
+ miocnak(q, mp, 0, EINVAL);
+ break;
+ }
+
+} /* conskbd_legacy_kbd_ioctl() */
+
+
+/*
+ * Service procedure for lower write queue.
+ * Puts things on the queue below us, if it lets us.
+ */
+static void
+conskbdlwserv(queue_t *q)
+{
+ register mblk_t *mp;
+
+ while (canput(q->q_next) && (mp = getq(q)) != NULL)
+ putnext(q, mp);
+
+} /* conskbdlwserv() */
+
+/*
+ * Put procedure for lower read queue.
+ * Pass everything up to minor device 0 if "directio" set, otherwise to minor
+ * device 1.
+ */
+static void
+conskbdlrput(queue_t *q, mblk_t *mp)
+{
+ conskbd_lower_queue_t *lqs;
+ struct iocblk *iocp;
+ Firm_event *fe;
+
+ DPRINTF(PRINT_L1, PRINT_MASK_ALL, ("conskbdlrput\n"));
+
+ switch (mp->b_datap->db_type) {
+
+ case M_FLUSH:
+ if (*mp->b_rptr == FLUSHR) {
+ flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */
+ *mp->b_rptr &= ~FLUSHR; /* it has been flushed */
+ }
+ if (*mp->b_rptr == FLUSHW) {
+ flushq(WR(q), FLUSHDATA);
+ qreply(q, mp); /* give the read queues a crack at it */
+ } else
+ freemsg(mp);
+ break;
+
+ case M_DATA:
+ if (conskbd.conskbd_bypassed == B_FALSE) {
+
+ fe = (Firm_event *)mp->b_rptr;
+
+ /*
+ * This is a workaround.
+ *
+ * According to HID specification, there are the
+ * following keycode mapping between PS2 and USB,
+ *
+ * PS2 AT-101 keycode(29) ---> USB(49)
+ * PS2 AT-102 keycode(42) ---> USB(50)
+ *
+ * However, the two keys, AT-101(29) and AT-102(42),
+ * have the same scancode,0x2B, in PS2 scancode SET1
+ * which we are using. The Kb8042 driver always
+ * recognizes the two keys as PS2(29) so that we could
+ * not know which is being pressed or released when we
+ * receive scancode 0x2B. Fortunately, the two keys can
+ * not co-exist in a specific layout. In other words,
+ * in the table of keycode-to-symbol mapping, either
+ * entry 49 or 50 is a hole. So, if we're processing a
+ * keycode 49, we look at the entry for 49. If it's
+ * HOLE, remap the key to 50; If we're processing a 50,
+ * look at the entry for 50. If it's HOLE, we remap
+ * the key to 49.
+ */
+ if (fe->id == 49 || fe->id == 50) {
+ if (conskbd_keyindex->k_normal[50] == HOLE)
+ fe->id = 49;
+ else
+ fe->id = 50;
+ }
+
+ /*
+ * Remember key state of each key of lower physical
+ * keyboard. When a keyboard is umplumbed from conskbd,
+ * we will check all key states. By then, we will fake
+ * a KEY_RELEASED message for each key in KEY_PRESSED
+ * state. Otherwise, upper module will treat these keys
+ * as held-down for ever.
+ */
+ iocp = (struct iocblk *)mp->b_rptr;
+ lqs = (conskbd_lower_queue_t *)q->q_ptr;
+ if (fe->value)
+ lqs->lqs_key_state[fe->id] = KEY_PRESSED;
+ else
+ lqs->lqs_key_state[fe->id] = KEY_RELEASED;
+
+ kbtrans_streams_key(conskbd.conskbd_kbtrans,
+ fe->id, fe->value ? KEY_PRESSED : KEY_RELEASED);
+ freemsg(mp);
+ } else {
+ if (conskbd.conskbd_directio)
+ putnext(conskbd_regqueue, mp);
+ else if (conskbd_consqueue != NULL)
+ putnext(conskbd_consqueue, mp);
+ else
+ freemsg(mp);
+ }
+ conskbd_idle_stamp = gethrestime_sec();
+ break;
+
+ case M_IOCACK:
+ case M_IOCNAK:
+ iocp = (struct iocblk *)mp->b_rptr;
+ lqs = (conskbd_lower_queue_t *)q->q_ptr;
+
+ DPRINTF(PRINT_L1, PRINT_MASK_ALL, ("conskbdlrput: "
+ "ACK/NAK - cmd 0x%x\n", iocp->ioc_cmd));
+
+ conskbd_lqs_ack_complete(lqs, mp);
+ break;
+
+ case M_ERROR:
+ case M_HANGUP:
+ default:
+ freemsg(mp); /* anything useful here? */
+ break;
+ }
+
+} /* conskbdlrput() */
+
+
+/* ARGSUSED */
+static int
+conskbd_kstat_update(kstat_t *ksp, int rw)
+{
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ conskbd_kstat.idle_sec.value.l = gethrestime_sec() - conskbd_idle_stamp;
+
+ return (0);
+
+} /* conskbd_kstat_update() */
+
+/*
+ * STREAMS architecuture provides guarantee that the ID of each
+ * message, iocblk.ioc_id, in a stream is unique. The following
+ * routine performes the task: When receive request from upstream,
+ * it saves the request in a global link list, clones the request,
+ * and then sends a copy of the request to each of lower queues
+ * which are plumbed into conskbd. And then, when receives responses
+ * from lower queues in conskbdlrput() routine, we can know the
+ * request matching received responses by searching the global linked
+ * list to find the request which has the same message ID of the
+ * response. Then, when all lower queues response this request, we
+ * give a response to upstreams based the following policy:
+ * If any one of lower queues acks our reuqest, then we return ack
+ * to upstreams; only if all lower queues nak our request, we return
+ * nak to upstreams. If all responses are nak, the error number of
+ * the first response is sent to upstream.
+ */
+static void
+conskbd_handle_downstream_msg(queue_t *q, mblk_t *mp)
+{
+ conskbd_pending_msg_t *msg;
+ conskbd_lower_queue_t *lqs;
+ struct iocblk *iocp;
+ mblk_t *clonemp;
+ int retry;
+
+ if (conskbd.conskbd_lqueue_nums == 0) {
+ miocnak(q, mp, 0, EINVAL);
+ return;
+ }
+
+ msg = (conskbd_pending_msg_t *)
+ kmem_zalloc(sizeof (conskbd_pending_msg_t), KM_SLEEP);
+ mutex_init(&msg->kpm_lock, NULL, MUTEX_DRIVER, NULL);
+ lqs = conskbd.conskbd_lqueue_list;
+ iocp = (struct iocblk *)mp->b_rptr;
+
+ ASSERT(iocp->ioc_cmd == CONSOPENPOLLEDIO ||
+ iocp->ioc_cmd == CONSCLOSEPOLLEDIO ||
+ iocp->ioc_cmd == KIOCCMD);
+
+ msg->kpm_upper_queue = q;
+ msg->kpm_req_msg = mp;
+ msg->kpm_req_id = iocp->ioc_id;
+ msg->kpm_req_cmd = iocp->ioc_cmd;
+ msg->kpm_req_nums = conskbd.conskbd_lqueue_nums;
+ conskbd_mux_enqueue_msg(msg);
+
+ for (retry = 0, lqs = conskbd.conskbd_lqueue_list; lqs; ) {
+
+ /*
+ * if a lower physical keyboard is not in polled I/O
+ * mode, we couldn't send CONSCLOSEPOLLEDIO to it,
+ * otherwise, system will panic.
+ */
+ if (iocp->ioc_cmd == CONSCLOSEPOLLEDIO &&
+ lqs->lqs_polledio == NULL) {
+ lqs = lqs->lqs_next;
+ msg->kpm_req_nums --;
+ retry = 0;
+ continue;
+ }
+
+ clonemp = copymsg(mp);
+ if (clonemp != NULL) {
+ if (putq(lqs->lqs_queue, clonemp) == 1) {
+ lqs = lqs->lqs_next;
+ retry = 0;
+ continue;
+ }
+
+ /*
+ * failed to invoke putq(), retry.
+ */
+ freemsg(clonemp);
+ }
+
+ /*
+ * During testing it was observed that occasionally
+ * copymsg() would fail during boot. The reason for
+ * these failures is unknown. Since we really want
+ * to successfully plumb up all the attached keyboards
+ * during boot we do a best effort here by retrying
+ * the copymsg() call in the hopes that it will
+ * succeeded upon subsequent invocations.
+ *
+ * If all the calls to copymsg() fails, it will cause
+ * the corresponding keyboard to be unavailable, or
+ * or behave weirdly,
+ *
+ * 1) for CONSOPENPOLLEDIO
+ * if copymsg()fails, the corresponding keyboard
+ * is not available in polled I/O mode once
+ * entering kmdb;
+ * 2) for CONSCLOSEPOLLEDIO
+ * if copymsg() fails, the corresponding keyboard
+ * is not available in normal mode once returning
+ * from kmdb;
+ * 3) for KIOCCMD
+ * 3.1) for KBD_CMD_NOBELL
+ * there's no beep in USB and PS2 keyboard,
+ * this ioctl actually disables the beep on
+ * system mainboard. Note that all the cloned
+ * messages sent down to lower queues do the
+ * same job for system mainboard. Therefore,
+ * even if we fail to send this ioctl to most
+ * of lower queues, the beep still would be
+ * disabled. So, no trouble exists here.
+ * 3.2) for others
+ * nothing;
+ *
+ * However, all cases could be resume next time when the
+ * same request comes again.
+ */
+ if (retry ++ >= 5) {
+ dev_t devt;
+ char path[MAXPATHLEN + 1];
+
+ devt = lqs->lqs_queue->q_stream->sd_vnode->v_rdev;
+ switch (iocp->ioc_cmd) {
+ case CONSOPENPOLLEDIO:
+ if (ddi_dev_pathname(devt, S_IFCHR,
+ path) == DDI_SUCCESS)
+ cmn_err(CE_WARN, "conskbd: "
+ "keyboard is not available"
+ " for system debugging: %s",
+ path);
+ break;
+
+ case CONSCLOSEPOLLEDIO:
+ if (ddi_dev_pathname(devt, S_IFCHR,
+ path) == DDI_SUCCESS)
+ cmn_err(CE_WARN, "conskbd: "
+ "keyboard is not available:"
+ " %s", path);
+ break;
+
+ default:
+ break;
+ }
+ msg->kpm_req_nums --;
+ lqs = lqs->lqs_next;
+ retry = 0;
+ }
+ }
+
+ if (msg->kpm_req_nums == 0) {
+ conskbd_mux_dequeue_msg(msg);
+ kmem_free(msg, sizeof (*msg));
+ miocnak(q, mp, 0, ENOMEM);
+ }
+
+} /* conskbd_handle_downstream_msg() */
+
+
+static void
+conskbd_ioc_plink(queue_t *q, mblk_t *mp)
+{
+ mblk_t *req;
+ queue_t *lowque;
+ struct iocblk *iocp;
+ struct linkblk *linkp;
+ conskbd_lower_queue_t *lqs;
+
+ ASSERT(mutex_owned(&conskbd_lq_lock));
+
+ lqs = kmem_zalloc(sizeof (*lqs), KM_SLEEP);
+ ASSERT(lqs->lqs_state == LQS_UNINITIALIZED);
+
+ iocp = (struct iocblk *)mp->b_rptr;
+ linkp = (struct linkblk *)mp->b_cont->b_rptr;
+ lowque = linkp->l_qbot;
+
+ lowque->q_ptr = (void *)lqs;
+ OTHERQ(lowque)->q_ptr = (void *)lqs;
+
+ lqs->lqs_queue = lowque;
+ lqs->lqs_pending_plink = mp;
+ lqs->lqs_pending_queue = q;
+
+ req = mkiocb(CONSSETKBDTYPE);
+ if (req == NULL) {
+ miocnak(q, mp, 0, ENOMEM);
+ lowque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ return;
+ }
+
+ req->b_cont = allocb(sizeof (int), BPRI_MED);
+ if (req->b_cont == NULL) {
+ freemsg(req);
+ miocnak(q, mp, 0, ENOMEM);
+ lowque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ return;
+ }
+
+ iocp->ioc_count = 0;
+ iocp->ioc_rval = 0;
+
+ *(int *)req->b_cont->b_wptr = KB_USB;
+ req->b_cont->b_wptr += sizeof (int);
+
+ lqs->lqs_state = LQS_KIOCTYPE_ACK_PENDING;
+
+ if (putq(lowque, req) != 1) {
+ freemsg(req);
+ miocnak(lqs->lqs_pending_queue,
+ lqs->lqs_pending_plink, 0, ENOMEM);
+ lowque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ }
+
+} /* conskbd_ioc_plink() */
+
+
+/*
+ * Every physical keyboard has a corresponding STREAMS queue. We call this
+ * queue lower queue. Every lower queue has a state, refer to conskbd.h file
+ * about "enum conskbd_lqs_state".
+ * The following routine is used to handle response messages from lower queue.
+ * When receiving ack/nak message from lower queue(s), the routine determines
+ * the passage for it according to the current state of this lower queue.
+ */
+static void
+conskbd_lqs_ack_complete(conskbd_lower_queue_t *lqs, mblk_t *mp)
+{
+ switch (lqs->lqs_state) {
+
+ /* S6: working in virtual keyboard mode, multi-keyboards are usable */
+ case LQS_INITIALIZED:
+ conskbd_mux_upstream_msg(lqs, mp);
+ break;
+
+ /* S5: working in legacy mode, only one keyboard is usable */
+ case LQS_INITIALIZED_LEGACY:
+ conskbd_legacy_upstream_msg(lqs, mp);
+ break;
+
+ /* S4: wait lower queue to acknowledge KIOCSLED message */
+ case LQS_KIOCSLED_ACK_PENDING:
+ conskbd_kiocsled_complete(lqs, mp);
+ break;
+
+ /* S3: wait lower queue to acknowledge KIOCLAYOUT message */
+ case LQS_KIOCLAYOUT_ACK_PENDING:
+ conskbd_kioclayout_complete(lqs, mp);
+ break;
+
+ /* S2: wait lower queue to acknowledge KIOCTRANS message */
+ case LQS_KIOCTRANS_ACK_PENDING:
+ conskbd_kioctrans_complete(lqs, mp);
+ break;
+
+ /* S1: wait lower queue to acknowledge KIOCTYPE message */
+ case LQS_KIOCTYPE_ACK_PENDING:
+ conskbd_kioctype_complete(lqs, mp);
+ break;
+
+ /* if reaching here, there must be a error */
+ default:
+ freemsg(mp);
+ cmn_err(CE_WARN, "conskbd: lqs_ack_complete() state error");
+ break;
+ }
+
+} /* conskbd_lqs_ack_complete() */
+
+
+static void
+conskbd_kioctype_complete(conskbd_lower_queue_t *lqs, mblk_t *mp)
+{
+ struct iocblk *iocp;
+ mblk_t *msg;
+ mblk_t *req;
+ queue_t *lowerque;
+
+ ASSERT(lqs->lqs_pending_plink);
+ ASSERT(lqs->lqs_state == LQS_KIOCTYPE_ACK_PENDING);
+
+ lowerque = lqs->lqs_queue;
+
+ switch (mp->b_datap->db_type) {
+ case M_IOCACK:
+ req = mkiocb(KIOCTRANS);
+ if (req == NULL) {
+ miocnak(lqs->lqs_pending_queue, lqs->lqs_pending_plink,
+ 0, ENOMEM);
+ lowerque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ freemsg(mp);
+ return;
+ }
+
+ req->b_cont = allocb(sizeof (int), BPRI_MED);
+ if (req->b_cont == NULL) {
+ miocnak(lqs->lqs_pending_queue, lqs->lqs_pending_plink,
+ 0, ENOMEM);
+ lowerque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ freemsg(req);
+ freemsg(mp);
+ return;
+ }
+
+ /* Set the translate mode to TR_UNTRANS_EVENT */
+ *(int *)req->b_cont->b_wptr = TR_UNTRANS_EVENT;
+ req->b_cont->b_wptr += sizeof (int);
+
+ /* Ready to handle the response to KIOCTRANS */
+ lqs->lqs_state = LQS_KIOCTRANS_ACK_PENDING;
+
+ if (putq(lowerque, req) != 1) {
+ freemsg(req);
+ miocnak(lqs->lqs_pending_queue,
+ lqs->lqs_pending_plink, 0, ENOMEM);
+ lowerque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ }
+ break;
+
+ case M_IOCNAK:
+ /*
+ * The lower keyboard driver can't mimic USB keyboard,
+ * that's say, the physical keyboard is an old one, such
+ * as TYPE 3/4/5 one. In this case, the virtual keyboard
+ * is disabled, and the data from lower keyboard driver
+ * will bypass the conskbd module.
+ */
+
+ /*
+ * if there is any other keyborad already linked under the
+ * conskbd, we reject the current one.
+ */
+ if (conskbd.conskbd_lqueue_nums > 0) {
+ iocp = (struct iocblk *)mp->b_rptr;
+ miocnak(lqs->lqs_pending_queue, lqs->lqs_pending_plink,
+ 0, iocp->ioc_error);
+ lowerque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ break;
+ }
+
+ /*
+ * Bypass the virutal keyboard for old hardware
+ */
+ conskbd.conskbd_bypassed = B_TRUE;
+
+ msg = lqs->lqs_pending_plink;
+ msg->b_datap->db_type = M_IOCACK;
+ iocp = (struct iocblk *)msg->b_rptr;
+ iocp->ioc_error = 0;
+
+ /*
+ * link this keyboard under conskbd
+ */
+ mutex_enter(&conskbd_lq_lock);
+ lqs->lqs_next = conskbd.conskbd_lqueue_list;
+ conskbd.conskbd_lqueue_list = lqs;
+ conskbd.conskbd_lqueue_nums++;
+ mutex_exit(&conskbd_lq_lock);
+
+ lqs->lqs_state = LQS_INITIALIZED_LEGACY;
+
+ qreply(lqs->lqs_pending_queue, lqs->lqs_pending_plink);
+ break;
+ }
+
+ freemsg(mp);
+
+} /* conskbd_kioctype_complete() */
+
+static void
+conskbd_kioctrans_complete(conskbd_lower_queue_t *lqs, mblk_t *mp)
+{
+ struct iocblk *iocp;
+ mblk_t *req;
+ queue_t *lowerque;
+
+ ASSERT(lqs->lqs_pending_plink != NULL);
+ ASSERT(lqs->lqs_state == LQS_KIOCTRANS_ACK_PENDING);
+
+ lowerque = lqs->lqs_queue;
+
+ switch (mp->b_datap->db_type) {
+ case M_IOCACK:
+ req = mkiocb(KIOCLAYOUT);
+ if (req == NULL) {
+ miocnak(lqs->lqs_pending_queue, lqs->lqs_pending_plink,
+ 0, ENOMEM);
+ lowerque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ freemsg(mp);
+ return;
+ }
+
+ req->b_cont = allocb(sizeof (int), BPRI_MED);
+ if (req->b_cont == NULL) {
+ miocnak(lqs->lqs_pending_queue, lqs->lqs_pending_plink,
+ 0, ENOMEM);
+ kmem_free(lqs, sizeof (*lqs));
+ freemsg(req);
+ freemsg(mp);
+ return;
+ }
+
+ /* waiting for response to KIOCLAYOUT */
+ lqs->lqs_state = LQS_KIOCLAYOUT_ACK_PENDING;
+ if (putq(lqs->lqs_queue, req) != 1) {
+ freemsg(req);
+ miocnak(lqs->lqs_pending_queue,
+ lqs->lqs_pending_plink, 0, ENOMEM);
+ lowerque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ }
+ break;
+
+ case M_IOCNAK:
+ iocp = (struct iocblk *)mp->b_rptr;
+ miocnak(lqs->lqs_pending_queue, lqs->lqs_pending_plink,
+ 0, iocp->ioc_error);
+ lowerque->q_ptr = NULL;
+ kmem_free(lqs, sizeof (*lqs));
+ break;
+ }
+
+ freemsg(mp);
+
+} /* conskbd_kioctrans_complete() */
+
+static void
+conskbd_kioclayout_complete(conskbd_lower_queue_t *lqs, mblk_t *mp)
+{
+ mblk_t *req;
+ int layout;
+ boolean_t fail;
+
+ ASSERT(lqs->lqs_pending_plink != NULL);
+ ASSERT(lqs->lqs_state == LQS_KIOCLAYOUT_ACK_PENDING);
+
+ switch (mp->b_datap->db_type) {
+ case M_IOCACK:
+ if (miocpullup(mp, sizeof (int)) == 0) {
+ layout = *(int *)mp->b_cont->b_rptr;
+ /*
+ * We just accept the layout of the first keyboard
+ * requesting to be linked under conskbd. If current
+ * keyboard is the first one, and if we get right
+ * layout from it, we set conskbd's layout
+ */
+ if (layout != -1 && conskbd.conskbd_layout == -1)
+ conskbd.conskbd_layout = layout;
+ }
+ break;
+
+
+ /* if fail, leave conskbd's layout as it is */
+ case M_IOCNAK:
+ break;
+ }
+
+ freemsg(mp);
+
+ fail = B_TRUE;
+ req = mkiocb(KIOCSLED);
+ if (req) {
+ req->b_cont = allocb(sizeof (uchar_t), BPRI_MED);
+ if (req->b_cont) {
+ *(uchar_t *)req->b_cont->b_wptr =
+ conskbd.conskbd_led_state;
+ req->b_cont->b_wptr += sizeof (uchar_t);
+
+ /* waiting for response to KIOCSLED */
+ lqs->lqs_state = LQS_KIOCSLED_ACK_PENDING;
+ if (putq(lqs->lqs_queue, req) == 1) {
+ fail = B_FALSE;
+ } else {
+ freemsg(req);
+ }
+
+ } else {
+ freemsg(req);
+ }
+ }
+
+ if (fail) {
+ /*
+ * If fail to allocate KIOCSLED message or put the message
+ * into lower queue, we immediately link current keyboard
+ * under conskbd. Thus, even if fails to set LED, this
+ * keyboard could be available.
+ */
+ conskbd_link_lower_queue(lqs);
+ }
+
+} /* conskbd_kioclayout_complete() */
+
+
+static void
+conskbd_kiocsled_complete(conskbd_lower_queue_t *lqs, mblk_t *mp)
+{
+ ASSERT(lqs->lqs_pending_plink != NULL);
+ ASSERT(lqs->lqs_state == LQS_KIOCSLED_ACK_PENDING);
+
+ /*
+ * Basically, failure of setting LED is not a fatal error,
+ * so we will plumb the lower queue into conskbd whether
+ * setting LED succeeds or fails.
+ */
+ freemsg(mp);
+ conskbd_link_lower_queue(lqs);
+
+} /* conskbd_kiocsled_complete() */
+
+
+static void
+conskbd_mux_upstream_msg(conskbd_lower_queue_t *lqs, mblk_t *mp)
+{
+ conskbd_pending_msg_t *msg;
+ struct iocblk *iocp;
+ int error;
+ dev_t devt;
+ char path[MAXPATHLEN + 1];
+
+ ASSERT(lqs->lqs_state == LQS_INITIALIZED);
+ msg = conskbd_mux_find_msg(mp);
+
+ if (!msg) {
+ /*
+ * Here, we just discard the responses to KIOCSLED request.
+ * Please refer to conskbd_streams_setled().
+ */
+ ASSERT(((struct iocblk *)mp->b_rptr)->ioc_cmd == KIOCSLED);
+ freemsg(mp);
+ return;
+ }
+
+ /*
+ * We use the b_next field of mblk_t structure to link all
+ * response coming from lower queues into a linkage list,
+ * and make use of the b_prev field to save a pointer to
+ * the lower queue from which the current response message
+ * comes.
+ */
+ ASSERT(mp->b_next == NULL && mp->b_prev == NULL);
+ mutex_enter(&msg->kpm_lock);
+ mp->b_next = msg->kpm_resp_list;
+ mp->b_prev = (mblk_t *)lqs;
+ msg->kpm_resp_list = mp;
+ msg->kpm_resp_nums ++;
+ mutex_exit(&msg->kpm_lock);
+
+ if (msg->kpm_resp_nums < msg->kpm_req_nums)
+ return;
+
+ ASSERT(msg->kpm_resp_nums == msg->kpm_req_nums);
+ ASSERT(mp == msg->kpm_resp_list);
+
+ conskbd_mux_dequeue_msg(msg);
+
+
+ /*
+ * Here, we have the policy that, if any one lower queue ACK
+ * our reuqest, then we return ACK to upstreams; only if all
+ * lower queues NAK our request, we return NAK to upstreams.
+ * if all responses are nak, the errno of the first response
+ * is sent to upstreams
+ */
+ ASSERT(mp->b_rptr);
+ error = ((struct iocblk *)mp->b_rptr)->ioc_error;
+
+ switch (msg->kpm_req_cmd) {
+ case CONSOPENPOLLEDIO:
+ /*
+ * Here, we can safely ignore the NAK message. If any one lower
+ * queue returns NAK, the pointer to the corresponding polledio
+ * structure will remain null, that's say lqs->lqs_polledio =
+ * null. When we need to invoke polled I/O interface, we will
+ * check if the pointer is null.
+ */
+ for (mp = msg->kpm_resp_list; mp; ) {
+ cons_polledio_t *polledio;
+
+ msg->kpm_resp_list = mp->b_next;
+ lqs = (conskbd_lower_queue_t *)mp->b_prev;
+ devt = lqs->lqs_queue->q_stream->sd_vnode->v_rdev;
+ if (mp->b_datap->db_type == M_IOCACK) {
+ polledio = *(struct cons_polledio **)
+ mp->b_cont->b_rptr;
+ if (polledio->cons_polledio_version ==
+ CONSPOLLEDIO_V1) {
+ lqs->lqs_polledio = polledio;
+ error = 0;
+ } else {
+ /*
+ * USB and PS2 keyboard drivers should
+ * use the same cons_polledio structure
+ * as conskbd.
+ */
+ if (ddi_dev_pathname(devt, S_IFCHR,
+ path) == DDI_SUCCESS) {
+ cmn_err(CE_WARN, "keyboard "
+ "driver does not support "
+ "system debugging: %s",
+ path);
+ }
+ error = EINVAL;
+ }
+ } else {
+ if (ddi_dev_pathname(devt, S_IFCHR, path) ==
+ DDI_SUCCESS) {
+ cmn_err(CE_WARN, "conskbd: keyboard is"
+ " not available for system"
+ " debugging: %s", path);
+ }
+ }
+ mp->b_next = NULL;
+ mp->b_prev = NULL;
+ freemsg(mp);
+ mp = msg->kpm_resp_list;
+ }
+
+ mp = msg->kpm_req_msg;
+ if (error == 0) {
+ *(struct cons_polledio **)mp->b_cont->b_rptr =
+ &conskbd.conskbd_polledio;
+ }
+ break;
+
+ case CONSCLOSEPOLLEDIO:
+ for (mp = msg->kpm_resp_list; mp; ) {
+ msg->kpm_resp_list = mp->b_next;
+ lqs = (conskbd_lower_queue_t *)mp->b_prev;
+ if (mp->b_datap->db_type == M_IOCACK) {
+ lqs->lqs_polledio = NULL;
+ error = 0;
+ } else {
+ devt =
+ lqs->lqs_queue->q_stream->sd_vnode->v_rdev;
+
+ if (ddi_dev_pathname(devt, S_IFCHR, path) ==
+ DDI_SUCCESS) {
+ cmn_err(CE_WARN, "conskbd: keyboard is"
+ " not available: %s", path);
+ }
+ }
+
+ mp->b_next = NULL;
+ mp->b_prev = NULL;
+ freemsg(mp);
+ mp = msg->kpm_resp_list;
+ }
+ break;
+
+ case KIOCCMD:
+ for (mp = msg->kpm_resp_list; mp; ) {
+ msg->kpm_resp_list = mp->b_next;
+
+ if (mp->b_datap->db_type == M_IOCACK)
+ error = 0;
+ mp->b_next = NULL;
+ mp->b_prev = NULL;
+ freemsg(mp);
+ mp = msg->kpm_resp_list;
+ }
+ break;
+
+ default: /* it is impossible to reach here */
+ cmn_err(CE_WARN, "conskbd: unexpected ioctl reply");
+ }
+
+ mp = msg->kpm_req_msg;
+ if (error == 0) {
+ mp->b_datap->db_type = M_IOCACK;
+ } else {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ iocp = (struct iocblk *)mp->b_rptr;
+ iocp->ioc_error = error;
+ qreply(msg->kpm_upper_queue, mp);
+ mutex_destroy(&msg->kpm_lock);
+ kmem_free(msg, sizeof (*msg));
+
+} /* conskbd_mux_upstream_msg() */
+
+
+static void
+conskbd_link_lower_queue(conskbd_lower_queue_t *lqs)
+{
+ struct iocblk *iocp;
+ mblk_t *msg;
+ int index;
+
+ ASSERT(lqs->lqs_pending_plink != NULL);
+
+ msg = lqs->lqs_pending_plink;
+ msg->b_datap->db_type = M_IOCACK;
+ iocp = (struct iocblk *)msg->b_rptr;
+ iocp->ioc_error = 0;
+
+ /*
+ * Now, link the lower queue under conskbd
+ */
+ mutex_enter(&conskbd_lq_lock);
+ conskbd.conskbd_lqueue_nums++;
+ lqs->lqs_next = conskbd.conskbd_lqueue_list;
+ conskbd.conskbd_lqueue_list = lqs;
+ for (index = 0; index < KBTRANS_KEYNUMS_MAX; index ++) {
+ lqs->lqs_key_state[index] = KEY_RELEASED;
+ }
+ lqs->lqs_state = LQS_INITIALIZED;
+ mutex_exit(&conskbd_lq_lock);
+ qreply(lqs->lqs_pending_queue, lqs->lqs_pending_plink);
+
+} /* conskbd_kiocsled_complete() */
+
+
+
+/*ARGSUSED*/
+static void
+conskbd_legacy_upstream_msg(conskbd_lower_queue_t *lqs, mblk_t *mp)
+{
+ struct iocblk *iocp;
+
+ ASSERT(lqs && lqs->lqs_state == LQS_INITIALIZED_LEGACY);
+
+ /*
+ * We assume that all of the ioctls are headed to the
+ * conskbd_regqueue if it is open. We are intercepting a few ioctls
+ * that we know belong to conskbd_consqueue, and sending them there.
+ * Any other, new ioctls that have to be routed to conskbd_consqueue
+ * should be added to this list.
+ */
+ iocp = (struct iocblk *)mp->b_rptr;
+
+ if ((iocp->ioc_cmd == CONSOPENPOLLEDIO) ||
+ (iocp->ioc_cmd == CONSCLOSEPOLLEDIO)) {
+
+ DPRINTF(PRINT_L1, PRINT_MASK_ALL,
+ ("conskbd_legacy_upstream_msg: "
+ "CONSOPEN/CLOSEPOLLEDIO ACK/NAK\n"));
+ putnext(conskbd_consqueue, mp);
+
+ } else if (conskbd_regqueue != NULL) {
+ DPRINTF(PRINT_L1, PRINT_MASK_ALL,
+ ("conskbd_legacy_upstream_msg: conskbd_regqueue != NULL"));
+
+ putnext(conskbd_regqueue, mp);
+
+ } else if (conskbd_consqueue != NULL) {
+ DPRINTF(PRINT_L1, PRINT_MASK_ALL,
+ ("conskbd_legacy_upstream_msg: conskbd_consqueue != NULL"));
+ putnext(conskbd_consqueue, mp);
+ } else {
+ /* if reached here, it must be a error */
+ cmn_err(CE_WARN,
+ "kb: no destination for IOCACK/IOCNAK!");
+ freemsg(mp);
+ }
+
+} /* conskbd_legacy_upstream_msg() */
+
+/*
+ * This routine is a callback routine for kbtrans module to set LED.
+ * Kbtrans will invoke it in two cases:
+ *
+ * 1) application initiated request
+ * A KIOCSLED ioctl is sent by an application. The ioctl will be
+ * be prcoessed by queue service procedure conskbduwsrv(), which
+ * in turn calls kbtrans to process the ioctl. Then kbtrans invokes
+ * conskbd_streams_setled() to set LED, after that, kbtrans will
+ * return an ACK message to upper module.
+ *
+ * 2) Kbtrans initiated the request
+ * When conskbd works in TR_ASCII translation mode, if anyone of
+ * CapsLock, NumberLock and Compose keys is pressed, kbtrans need
+ * to set LED. In this case, there is no ioctl from upper module.
+ * There is no requirement to send response to somebody.
+ *
+ * In first case, kbtrans will send response to upper module; and in the
+ * second, we don't need to send response. So conskbd_streams_setled()
+ * has no return value.
+ */
+static void
+conskbd_streams_setled(struct kbtrans_hardware *hw, int led_state)
+{
+ conskbd_state_t *conskbdp = (conskbd_state_t *)hw;
+ conskbd_lower_queue_t *lqs;
+ mblk_t *req;
+
+ ASSERT(&conskbd == conskbdp);
+
+ if (led_state == -1)
+ return;
+
+ conskbdp->conskbd_led_state = led_state;
+
+ /*
+ * Basically, failing to set LED is not a fatal error, we just skip
+ * it if this happens.
+ */
+ for (lqs = conskbdp->conskbd_lqueue_list; lqs; lqs = lqs->lqs_next) {
+ req = mkiocb(KIOCSLED);
+
+ if (!req) {
+ continue;
+ }
+
+ req->b_cont = allocb(sizeof (uchar_t), BPRI_MED);
+ if (!req->b_cont) {
+ freemsg(req);
+ continue;
+ }
+ *(uchar_t *)req->b_cont->b_wptr = led_state;
+ req->b_cont->b_wptr += sizeof (uchar_t);
+ if (putq(lqs->lqs_queue, req) != 1)
+ freemsg(req);
+ }
+
+} /* conskbd_streams_setled() */
+
+static void
+conskbd_polledio_setled(struct kbtrans_hardware *hw, int led_state)
+{
+ conskbd_state_t *conskbdp = (conskbd_state_t *)hw;
+ struct cons_polledio *cb;
+ conskbd_lower_queue_t *lqs;
+
+ for (lqs = conskbdp->conskbd_lqueue_list; lqs; lqs = lqs->lqs_next) {
+ cb = lqs->lqs_polledio;
+ if ((cb != NULL) && (cb->cons_polledio_setled != NULL)) {
+ cb->cons_polledio_setled(cb->cons_polledio_argument,
+ led_state);
+ }
+ }
+
+} /* conskbd_polledio_setled() */
+
+static boolean_t
+conskbd_polled_keycheck(struct kbtrans_hardware *hw,
+ kbtrans_key_t *keycode, enum keystate *state)
+{
+ conskbd_state_t *conskbdp = (conskbd_state_t *)hw;
+ struct cons_polledio *cb;
+ conskbd_lower_queue_t *lqs;
+ boolean_t ret = B_FALSE;
+
+ for (ret = B_FALSE, lqs = conskbdp->conskbd_lqueue_list; lqs != NULL;
+ lqs = lqs->lqs_next) {
+ cb = lqs->lqs_polledio;
+ if ((cb != NULL) &&
+ (cb->cons_polledio_keycheck != NULL)) {
+ ret = cb->cons_polledio_keycheck(
+ cb->cons_polledio_argument, keycode, state);
+ }
+
+ /* Get a char from lower queue(hardware) ? */
+ if (ret == B_TRUE) {
+ break;
+ }
+ }
+
+ return (ret);
+
+} /* conskbd_polled_keycheck() */
+
+static boolean_t
+conskbd_override_kbtrans(queue_t *q, mblk_t *mp)
+{
+ struct iocblk *iocp;
+ int directio;
+ int error;
+
+ if (mp->b_datap->db_type != M_IOCTL)
+ return (B_FALSE);
+
+ iocp = (struct iocblk *)mp->b_rptr;
+
+ switch (iocp->ioc_cmd) {
+ case KIOCGDIRECT: {
+ /*
+ * Don't let the kbtrans-based code see this; it will
+ * respond incorrectly.
+ */
+ register mblk_t *datap;
+
+ if ((datap = allocb((int)sizeof (int), BPRI_MED)) == NULL) {
+ miocnak(q, mp, 0, ENOMEM);
+ return (B_TRUE);
+ }
+
+ *(int *)datap->b_wptr = conskbd.conskbd_directio;
+ datap->b_wptr += sizeof (int);
+ if (mp->b_cont) {
+ freemsg(mp->b_cont);
+ mp->b_cont = NULL;
+ }
+ mp->b_cont = datap;
+ miocack(q, mp, sizeof (int), 0);
+ return (B_TRUE);
+ }
+
+ case KIOCSDIRECT:
+ /*
+ * Peek at this, set our variables, and then let the kbtrans
+ * based code see it and respond to it.
+ */
+ error = miocpullup(mp, sizeof (int));
+ if (error != 0) {
+ return (B_FALSE);
+ }
+
+ directio = *(int *)mp->b_cont->b_rptr;
+ if (directio != 0 && directio != 1) {
+ miocnak(q, mp, 0, EINVAL);
+ return (B_TRUE);
+ }
+ conskbd.conskbd_directio = directio;
+
+ if (conskbd.conskbd_directio) {
+ kbtrans_streams_set_queue(
+ conskbd.conskbd_kbtrans, conskbd_regqueue);
+ } else {
+ kbtrans_streams_set_queue(
+ conskbd.conskbd_kbtrans, conskbd_consqueue);
+ }
+
+ /*
+ * Let the kbtrans-based code see this and respond to it.
+ */
+ return (B_FALSE);
+
+ default:
+ return (B_FALSE);
+ }
+
+} /* conskbd_override_kbtrans() */
+
+
+static void
+conskbd_polledio_enter(struct cons_polledio_arg *arg)
+{
+ conskbd_state_t *conskbdp;
+ struct cons_polledio *cb;
+ conskbd_lower_queue_t *lqs;
+
+ conskbdp = (conskbd_state_t *)arg;
+ for (lqs = conskbdp->conskbd_lqueue_list; lqs; lqs = lqs->lqs_next) {
+ cb = lqs->lqs_polledio;
+ if ((cb != NULL) && (cb->cons_polledio_enter != NULL)) {
+ cb->cons_polledio_enter(cb->cons_polledio_argument);
+ }
+ }
+
+} /* conskbd_polledio_enter() */
+
+static void
+conskbd_polledio_exit(struct cons_polledio_arg *arg)
+{
+ conskbd_state_t *conskbdp;
+ struct cons_polledio *cb;
+ conskbd_lower_queue_t *lqs;
+
+ conskbdp = (conskbd_state_t *)arg;
+ for (lqs = conskbdp->conskbd_lqueue_list; lqs; lqs = lqs->lqs_next) {
+ cb = lqs->lqs_polledio;
+ if ((cb != NULL) && (cb->cons_polledio_exit != NULL)) {
+ cb->cons_polledio_exit(cb->cons_polledio_argument);
+ }
+ }
+
+} /* conskbd_polledio_exit() */
+
+static int
+conskbd_polledio_getchar(struct cons_polledio_arg *arg)
+{
+ conskbd_state_t *conskbdp;
+
+ conskbdp = (conskbd_state_t *)arg;
+
+ return (kbtrans_getchar(conskbdp->conskbd_kbtrans));
+
+} /* conskbd_polledio_getchar() */
+
+static int
+conskbd_polledio_ischar(struct cons_polledio_arg *arg)
+{
+ conskbd_state_t *conskbdp;
+
+ conskbdp = (conskbd_state_t *)arg;
+
+ return (kbtrans_ischar(conskbdp->conskbd_kbtrans));
+
+} /* conskbd_polledio_ischar() */
+
+
+static void
+conskbd_mux_enqueue_msg(conskbd_pending_msg_t *msg)
+{
+ mutex_enter(&conskbd_msgq_lock);
+ msg->kpm_next = conskbd_msg_queue;
+ conskbd_msg_queue = msg;
+ mutex_exit(&conskbd_msgq_lock);
+
+} /* conskbd_mux_enqueue_msg() */
+
+/*
+ * the messages in conskbd_msg_queue we just enqueue
+ */
+static conskbd_pending_msg_t *
+conskbd_mux_find_msg(mblk_t *mp)
+{
+ conskbd_pending_msg_t *msg;
+ struct iocblk *iocp;
+ uint_t id;
+
+ mutex_enter(&conskbd_msgq_lock);
+ msg = conskbd_msg_queue;
+
+ iocp = (struct iocblk *)mp->b_rptr;
+ ASSERT(iocp);
+ id = iocp->ioc_id;
+ while (msg && msg->kpm_req_id != id) {
+ msg = msg->kpm_next;
+ }
+ mutex_exit(&conskbd_msgq_lock);
+
+ return (msg);
+
+} /* conskbd_mux_find_msg() */
+
+
+static void
+conskbd_mux_dequeue_msg(conskbd_pending_msg_t *msg)
+{
+ conskbd_pending_msg_t *prev;
+ conskbd_pending_msg_t *p;
+
+ mutex_enter(&conskbd_msgq_lock);
+ prev = conskbd_msg_queue;
+
+ for (p = prev; p != msg; p = p->kpm_next)
+ prev = p;
+ ASSERT(p && p == msg);
+ if (prev == p) {
+ conskbd_msg_queue = msg->kpm_next;
+ } else {
+ prev->kpm_next = p->kpm_next;
+ }
+ p->kpm_next = NULL;
+ mutex_exit(&conskbd_msgq_lock);
+
+} /* conskbd_mux_dequeue_msg() */
+
+#ifdef DEBUG
+/*ARGSUSED*/
+void
+conskbd_dprintf(const char *fmt, ...)
+{
+ char buf[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ cmn_err(CE_CONT, "conskbd: %s", buf);
+
+} /* conskbd_dprintf() */
+#endif