conskbd.c revision 78575d6e9938b80dc58047b527116eb92d5a3f33
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2006 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/vuid_event.h>
extern struct keyboard *kbtrans_usbkb_maptab_init(void);
extern void kbtrans_usbkb_maptab_fini(struct keyboard **);
/*
* Module linkage routines for the kernel
*/
/*
* STREAMS queue processing procedures
*/
static void conskbduwsrv(queue_t *);
static void conskbdlwserv(queue_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 */
};
/*
* 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 */
NULL /* devo_power */
};
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
"conskbd multiplexer driver %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, ...);
(((l) >= conskbd_errlevel) && ((m) & conskbd_errmask) ? \
conskbd_dprintf args : \
(void) 0)
/*
* Severity levels for printing
*/
#define PRINT_L0 0 /* print every message */
/*
* Masks
*/
#define PRINT_MASK_ALL 0xFFFFFFFFU
#else
#endif
#endif
/*
* Module global data are protected by outer perimeter. Modifying
* these global data is executed in outer perimeter exclusively.
* Except in conskbdopen() and conskbdclose(), which are entered
* exclusively (Refer to D_MTOCEXCL flag), all changes for the
* global variables are protected by qwriter().
*/
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
* will be easier to add new statistics later.
*/
static struct {
} conskbd_kstat = {
{ "idle_sec", KSTAT_DATA_LONG, }
};
/*
* Local routines prototypes
*/
static int conskbd_kstat_update(kstat_t *, int);
static mblk_t *conskbd_alloc_firm_event(int, int);
static void conskbd_mux_enqueue_msg(conskbd_pending_msg_t *);
static void conskbd_mux_dequeue_msg(conskbd_pending_msg_t *);
static void conskbd_polledio_enter(cons_polledio_arg_t);
static void conskbd_polledio_exit(cons_polledio_arg_t);
static int conskbd_polledio_ischar(cons_polledio_arg_t);
static int conskbd_polledio_getchar(cons_polledio_arg_t);
static void conskbd_polledio_setled(struct kbtrans_hardware *, int);
static void conskbd_streams_setled(struct kbtrans_hardware *, int);
static boolean_t
conskbd_polled_keycheck(struct kbtrans_hardware *,
kbtrans_key_t *, enum keystate *);
/*
* Callbacks needed by kbtrans
*/
static struct kbtrans_callbacks conskbd_callbacks = {
};
/*
* Single private "global" lock for the few rare conditions
* we want single-threaded.
*/
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 supported.
*/
static conskbd_state_t conskbd = { 0 };
/* This variable backs up the layout state for non-self-ID keyboards */
static int kbd_layout_bak = 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;
if (error != 0) {
return (error);
}
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;
if (error != 0)
return (error);
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() */
/*
* 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
{
switch (cmd) {
case DDI_ATTACH:
break;
default:
return (DDI_FAILURE);
}
1) == DDI_FAILURE)) {
return (DDI_FAILURE);
}
conskbd_dip = devi;
sizeof (conskbd_kstat) / sizeof (kstat_named_t),
if (ksp) {
}
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
{
return (DDI_FAILURE);
} /* conskbd_detach() */
/* ARGSUSED */
static int
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
{
int err;
if (unit == 0) {
/*
*/
conskbd_regqueue = q;
qprocson(q);
return (0);
} else if (unit != 1) {
/* we don't do that under Bozo's Big Tent */
return (ENODEV);
}
/*
* Check if already initialized
*/
if (conskbd_consqueue != NULL)
return (0);
/*
* Opening the device to be linked under the console.
*/
conskbd_consqueue = q;
/*
* initialize kbtrans module for conskbd
*/
if (err != 0)
return (err);
(int (*)(cons_polledio_arg_t)) conskbd_polledio_getchar;
qprocson(q);
return (0);
} /* conskbdopen() */
/*ARGSUSED*/
static int
{
if (q == conskbd_regqueue) {
/* switch the input stream back to conskbd_consqueue */
qprocsoff(q);
/*
* If there are any pending ioctls which conskbd hasn't
* responded to yet, remove them from conskbd_msg_queue.
* Otherwise, we might send the response to a nonexistent
* closed queue. Refer to: conskbd_mux_upstream_msg().
*/
if (pmsg->kpm_upper_queue == q) {
else
}
} else {
}
}
} 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);
}
return (0);
} /* conskbdclose() */
/*
* 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)
{
enum kbtrans_message_response ret;
/*
* if the virtual keyboard is supported
*/
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.
*/
switch (ret) {
case KBTRANS_MESSAGE_HANDLED:
continue;
break;
}
}
case M_IOCTL:
conskbd_ioctl(q, mp);
break;
case M_FLUSH:
}
/*
* 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.
*/
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)) {
} else {
}
break;
default:
/*
* Pass an error message up.
*/
}
}
} /* end of while */
} /* conskbduwsrv() */
static void
{
int error = 0;
case I_LINK:
case I_PLINK:
/*
* A legacy keyboard can NOT be connected to conskbd together
* with other keyboards. So when a legacy keyboard is already
* linked under conkbd, we just reject all others.
*/
break;
}
break;
case I_UNLINK:
case I_PUNLINK:
break;
case KIOCSKABORTEN:
/*
* Check if privileged
*/
return;
}
if (error != 0) {
return;
}
break;
default:
} else {
}
}
} /* conskbd_ioctl() */
static void
{
int cmd;
int error = 0;
case KIOCLAYOUT:
break;
}
else
break;
case KIOCSLAYOUT:
break;
}
break;
case CONSOPENPOLLEDIO:
if (error != 0) {
break;
}
break;
}
break;
case CONSCLOSEPOLLEDIO:
break;
}
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.
*/
break;
case KIOCCMD:
break;
}
if (cmd == KBD_CMD_GETLAYOUT) {
return;
}
else
return;
}
break;
default:
break;
}
} /* conskbd_virtual_kbd_ioctl() */
static void
{
int error = 0;
case KIOCGDIRECT: {
break;
}
}
break;
}
case KIOCSDIRECT:
if (error != 0) {
break;
}
/*
* 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) {
return;
}
break;
}
break;
default:
/*
* Pass this through, if there's something to pass it
* through to; otherwise, reject it.
*/
if (conskbd.conskbd_lqueue_nums > 0) {
return;
}
break;
}
/* nobody below us; reject it */
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)
{
} /* conskbdlwserv() */
/*
* Put procedure for lower read queue.
* Pass everything up to minor device 0 if "directio" set, otherwise to minor
* device 1.
*/
static void
{
Firm_event *fe;
case M_FLUSH:
}
} else
break;
case M_DATA:
/*
* 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.
*/
else
}
/*
* 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.
*/
else
} else {
if (conskbd.conskbd_directio)
else if (conskbd_consqueue != NULL)
else
}
break;
case M_IOCACK:
case M_IOCNAK:
break;
case M_ERROR:
case M_HANGUP:
default:
break;
}
} /* conskbdlrput() */
/* ARGSUSED */
static int
{
if (rw == KSTAT_WRITE)
return (EACCES);
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
{
int retry;
if (conskbd.conskbd_lqueue_nums == 0) {
return;
}
msg = (conskbd_pending_msg_t *)
msg->kpm_upper_queue = q;
/*
* if a lower physical keyboard is not in polled I/O
* mode, we couldn't send CONSCLOSEPOLLEDIO to it,
* otherwise, system will panic.
*/
msg->kpm_req_nums --;
retry = 0;
continue;
}
retry = 0;
continue;
}
/*
* failed to invoke putq(), retry.
*/
}
/*
* 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) {
case CONSOPENPOLLEDIO:
path) == DDI_SUCCESS)
"keyboard is not available"
" for system debugging: %s",
path);
break;
case CONSCLOSEPOLLEDIO:
path) == DDI_SUCCESS)
"keyboard is not available:"
" %s", path);
break;
default:
break;
}
msg->kpm_req_nums --;
retry = 0;
}
}
if (msg->kpm_req_nums == 0) {
}
} /* conskbd_handle_downstream_msg() */
static void
{
lqs->lqs_pending_queue = q;
return;
}
return;
}
}
} /* conskbd_ioc_plink() */
static void
{
int index;
else
if (conskbd.conskbd_lqueue_nums == 0) {
}
}
return;
}
}
} /* conskbd_ioc_punlink() */
/*
* 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.
* the passage for it according to the current state of this lower queue.
*/
static void
{
/* S6: working in virtual keyboard mode, multi-keyboards are usable */
case LQS_INITIALIZED:
break;
/* S5: working in legacy mode, only one keyboard is usable */
case LQS_INITIALIZED_LEGACY:
break;
case LQS_KIOCSLED_ACK_PENDING:
break;
/* S3: wait lower queue to acknowledge KIOCLAYOUT message */
break;
/* S2: wait lower queue to acknowledge KIOCTRANS message */
break;
/* S1: wait lower queue to acknowledge KIOCTYPE message */
case LQS_KIOCTYPE_ACK_PENDING:
break;
/* if reaching here, there must be a error */
default:
break;
}
} /* conskbd_lqs_ack_complete() */
static void
{
case M_IOCACK:
goto err_exit;
}
goto err_exit;
}
/* Set the translate mode to TR_UNTRANS_EVENT */
/* Ready to handle the response to KIOCTRANS */
goto err_exit;
}
return;
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) {
goto err_exit;
}
/*
* link this keyboard under conskbd.
*/
return;
}
} /* conskbd_kioctype_complete() */
static void
{
case M_IOCACK:
goto err_exit;
}
goto err_exit;
}
/* waiting for response to KIOCLAYOUT */
goto err_exit;
}
return;
case M_IOCNAK:
goto err_exit;
}
} /* conskbd_kioctrans_complete() */
/*
* Allocate a firm event
*/
static mblk_t *
{
Firm_event *fe;
}
return (mb);
}
static void
{
int layout;
case M_IOCACK:
if (miocpullup(mp, sizeof (int)) == 0) {
/*
* 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 == 0) {
} else {
if (layout == kbd_layout_bak) {
break;
}
if ((req = conskbd_alloc_firm_event(
if (conskbd.conskbd_directio)
req);
else if (conskbd_consqueue
!= NULL)
req);
}
}
}
}
break;
/* if fail, leave conskbd's layout as it is */
case M_IOCNAK:
break;
}
else
if (req) {
}
/* waiting for response to KIOCSLED */
} else {
}
} else {
}
}
if (fail) {
/*
* the message into lower queue, we immediately link
* current keyboard under conskbd. Thus, even if fails
*/
} else {
}
} /* conskbd_kioclayout_complete() */
static void
{
int led_state;
case M_IOCACK:
}
break;
/* if fail, leave conskbd's led_state as it is */
case M_IOCNAK:
break;
}
}
/*
* error, so we will plumb the lower queue into conskbd whether
*/
} /* conskbd_kiocsled_complete() */
static void
{
int error;
if (!msg) {
/*
* Here we discard the response if:
*
* 1. It's an KIOCSLED request; see conskbd_streams_setled().
* 2. The application has already closed the upper stream;
* see conskbdclose()
*/
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.
*/
msg->kpm_resp_nums ++;
return;
/*
* 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
*/
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.
*/
polledio = *(struct cons_polledio **)
if (polledio->cons_polledio_version ==
error = 0;
} else {
/*
* USB and PS2 keyboard drivers should
* use the same cons_polledio structure
* as conskbd.
*/
path) == DDI_SUCCESS) {
"driver does not support "
"system debugging: %s",
path);
}
}
} else {
DDI_SUCCESS) {
" not available for system"
" debugging: %s", path);
}
}
}
if (error == 0) {
}
break;
case CONSCLOSEPOLLEDIO:
error = 0;
} else {
devt =
DDI_SUCCESS) {
" not available: %s", path);
}
}
}
break;
case KIOCCMD:
error = 0;
}
break;
default: /* it is impossible to reach here */
}
if (error == 0) {
} else {
}
} /* conskbd_mux_upstream_msg() */
static void
{
/*
* Bypass the virutal keyboard for old hardware,
* Now, only current legacy keyboard can be linked
* under conskbd
*/
/*
* Link the lower queue under conskbd
*/
} /* conskbd_link_lowque_legacy() */
static void
{
int index;
/*
* Now, link the lower queue under conskbd
*/
}
} /* conskbd_link_lowque_virt() */
/*ARGSUSED*/
static void
{
/*
* 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.
*/
("conskbd_legacy_upstream_msg: "
"CONSOPEN/CLOSEPOLLEDIO ACK/NAK\n"));
} else if (conskbd_regqueue != NULL) {
("conskbd_legacy_upstream_msg: conskbd_regqueue != NULL"));
} else if (conskbd_consqueue != NULL) {
("conskbd_legacy_upstream_msg: conskbd_consqueue != NULL"));
} else {
/* if reached here, it must be a error */
}
} /* 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
{
if (led_state == -1)
return;
/*
* Basically, failing to set LED is not a fatal error, we just skip
* it if this happens.
*/
if (!req) {
continue;
}
continue;
}
}
} /* conskbd_streams_setled() */
static void
{
struct cons_polledio *cb;
}
}
} /* conskbd_polledio_setled() */
static boolean_t
{
struct cons_polledio *cb;
}
/* Get a char from lower queue(hardware) ? */
/* A legacy keyboard ? */
break;
/*
* This is the PS2 scancode 0x2B -> USB(49) /
* USB(50) keycode mapping workaround, for
* polled mode.
*
* There are two possible USB keycode mappings
* for PS2 scancode 0x2B and this workaround
* makes sure that we use the USB keycode that
* does not end up being mapped to a HOLE key
* using the current keyboard translation
* tables.
*
* See conskbdlrput() for a detailed
* explanation of the problem.
*/
*keycode = 49;
else
*keycode = 50;
}
break;
}
}
return (ret);
} /* conskbd_polled_keycheck() */
static boolean_t
{
int directio;
int error;
return (B_FALSE);
case KIOCGDIRECT: {
/*
* Don't let the kbtrans-based code see this; it will
* respond incorrectly.
*/
return (B_TRUE);
}
}
return (B_TRUE);
}
case KIOCSDIRECT:
/*
* Peek at this, set our variables, and then let the kbtrans
* based code see it and respond to it.
*/
if (error != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
if (conskbd.conskbd_directio) {
} else {
}
/*
* Let the kbtrans-based code see this and respond to it.
*/
return (B_FALSE);
default:
return (B_FALSE);
}
} /* conskbd_override_kbtrans() */
static void
{
struct cons_polledio *cb;
}
}
} /* conskbd_polledio_enter() */
static void
{
struct cons_polledio *cb;
}
}
} /* conskbd_polledio_exit() */
static int
{
} /* conskbd_polledio_getchar() */
static int
{
} /* conskbd_polledio_ischar() */
static void
{
} /* conskbd_mux_enqueue_msg() */
/*
* the messages in conskbd_msg_queue we just enqueue
*/
static conskbd_pending_msg_t *
{
}
return (msg);
} /* conskbd_mux_find_msg() */
static void
{
prev = p;
if (prev == p) {
} else {
}
} /* conskbd_mux_dequeue_msg() */
#ifdef DEBUG
/*ARGSUSED*/
void
conskbd_dprintf(const char *fmt, ...)
{
char buf[256];
} /* conskbd_dprintf() */
#endif