#include <sys/usb/usba/usbai_version.h>
#define KEYMAP_SIZE_VARIABLE
#include <sys/usb/usba.h>
#include <sys/usb/clients/hid/hid.h>
#include <sys/usb/clients/hid/hid_polled.h>
#include <sys/usb/clients/hidparser/hidparser.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/kbio.h>
#include <sys/vuid_event.h>
#include <sys/kbd.h>
#include <sys/consdev.h>
#include <sys/kbtrans.h>
#include <sys/usb/clients/usbkbm/usbkbm.h>
#include <sys/beep.h>
#include <sys/inttypes.h>
uint_t usbkbm_errmask = (uint_t)PRINT_MASK_ALL;
uint_t usbkbm_errlevel = USB_LOG_L2;
static usb_log_handle_t usbkbm_log_handle;
typedef void (*process_key_callback_t)(usbkbm_state_t *, int, enum keystate);
static void usbkbm_streams_setled(struct kbtrans_hardware *, int);
static void usbkbm_polled_setled(struct kbtrans_hardware *, int);
static boolean_t usbkbm_polled_keycheck(struct kbtrans_hardware *,
int *, enum keystate *);
static void usbkbm_poll_callback(usbkbm_state_t *, int, enum keystate);
static void usbkbm_streams_callback(usbkbm_state_t *, int, enum keystate);
static void usbkbm_unpack_usb_packet(usbkbm_state_t *, process_key_callback_t,
uchar_t *);
static boolean_t usbkbm_is_modkey(uchar_t);
static void usbkbm_reioctl(void *);
static int usbkbm_polled_getchar(cons_polledio_arg_t);
static boolean_t usbkbm_polled_ischar(cons_polledio_arg_t);
static void usbkbm_polled_enter(cons_polledio_arg_t);
static void usbkbm_polled_exit(cons_polledio_arg_t);
static void usbkbm_mctl_receive(queue_t *, mblk_t *);
static enum kbtrans_message_response usbkbm_ioctl(queue_t *, mblk_t *);
static int usbkbm_kioccmd(usbkbm_state_t *, mblk_t *, char, size_t *);
static void usbkbm_usb2pc_xlate(usbkbm_state_t *, int, enum keystate);
static void usbkbm_wrap_kbtrans(usbkbm_state_t *, int, enum keystate);
static int usbkbm_get_input_format(usbkbm_state_t *);
static int usbkbm_get_vid_pid(usbkbm_state_t *);
static int usbkbm_open(queue_t *, dev_t *, int, int, cred_t *);
static int usbkbm_close(queue_t *, int, cred_t *);
static int usbkbm_wput(queue_t *, mblk_t *);
static int usbkbm_rput(queue_t *, mblk_t *);
static int usbkbm_rsrv(queue_t *);
static ushort_t usbkbm_get_state(usbkbm_state_t *);
static void usbkbm_get_scancode(usbkbm_state_t *, int *, enum keystate *);
static struct keyboard *usbkbm_keyindex;
extern void space_free(char *);
extern uintptr_t space_fetch(char *);
extern int space_store(char *, uintptr_t);
extern struct keyboard *kbtrans_usbkb_maptab_init(void);
extern void kbtrans_usbkb_maptab_fini(struct keyboard **);
extern keymap_entry_t kbtrans_keycode_usb2pc(int);
struct kbtrans_callbacks kbd_usb_callbacks = {
usbkbm_streams_setled,
usbkbm_polled_setled,
usbkbm_polled_keycheck,
};
static uchar_t usbkbm_led_state = 0;
static uint16_t usbkbm_layout = 0;
void (*usbkbm_xlate[2])(usbkbm_state_t *, int, enum keystate) = {
usbkbm_wrap_kbtrans,
usbkbm_usb2pc_xlate
};
static struct streamtab usbkbm_info;
static struct fmodsw fsw = {
"usbkbm",
&usbkbm_info,
D_MP | D_MTPERMOD
};
static struct modlstrmod modlstrmod = {
&mod_strmodops,
"USB keyboard streams 1.44",
&fsw
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modlstrmod,
NULL
};
int
_init(void)
{
int rval = mod_install(&modlinkage);
usbkbm_save_state_t *sp;
if (rval != 0) {
return (rval);
}
usbkbm_keyindex = kbtrans_usbkb_maptab_init();
usbkbm_log_handle = usb_alloc_log_hdl(NULL, "usbkbm",
&usbkbm_errlevel, &usbkbm_errmask, NULL, 0);
sp = (usbkbm_save_state_t *)space_fetch("SUNW,usbkbm_state");
if (sp == NULL) {
return (0);
}
usbkbm_led_state = sp->usbkbm_save_led;
usbkbm_layout = sp->usbkbm_layout;
usbkbm_keyindex->k_abort1 =
sp->usbkbm_save_keyindex.k_abort1;
usbkbm_keyindex->k_abort2 =
sp->usbkbm_save_keyindex.k_abort2;
usbkbm_keyindex->k_newabort1 =
sp->usbkbm_save_keyindex.k_newabort1;
usbkbm_keyindex->k_newabort2 =
sp->usbkbm_save_keyindex.k_newabort2;
bcopy(sp->usbkbm_save_keyindex.k_normal,
usbkbm_keyindex->k_normal, USB_KEYTABLE_SIZE);
bcopy(sp->usbkbm_save_keyindex.k_shifted,
usbkbm_keyindex->k_shifted, USB_KEYTABLE_SIZE);
bcopy(sp->usbkbm_save_keyindex.k_caps,
usbkbm_keyindex->k_caps, USB_KEYTABLE_SIZE);
bcopy(sp->usbkbm_save_keyindex.k_altgraph,
usbkbm_keyindex->k_altgraph, USB_KEYTABLE_SIZE);
bcopy(sp->usbkbm_save_keyindex.k_numlock,
usbkbm_keyindex->k_numlock, USB_KEYTABLE_SIZE);
bcopy(sp->usbkbm_save_keyindex.k_control,
usbkbm_keyindex->k_control, USB_KEYTABLE_SIZE);
bcopy(sp->usbkbm_save_keyindex.k_up,
usbkbm_keyindex->k_up, USB_KEYTABLE_SIZE);
kmem_free(sp->usbkbm_save_keyindex.k_normal,
USB_KEYTABLE_SIZE);
kmem_free(sp->usbkbm_save_keyindex.k_shifted,
USB_KEYTABLE_SIZE);
kmem_free(sp->usbkbm_save_keyindex.k_caps,
USB_KEYTABLE_SIZE);
kmem_free(sp->usbkbm_save_keyindex.k_altgraph,
USB_KEYTABLE_SIZE);
kmem_free(sp->usbkbm_save_keyindex.k_numlock,
USB_KEYTABLE_SIZE);
kmem_free(sp->usbkbm_save_keyindex.k_control,
USB_KEYTABLE_SIZE);
kmem_free(sp->usbkbm_save_keyindex.k_up,
USB_KEYTABLE_SIZE);
kmem_free(sp, sizeof (usbkbm_save_state_t));
space_free("SUNW,usbkbm_state");
return (0);
}
int
_fini(void)
{
usbkbm_save_state_t *sp;
int sval;
int rval;
sp = kmem_alloc(sizeof (usbkbm_save_state_t), KM_SLEEP);
sval = space_store("SUNW,usbkbm_state", (uintptr_t)sp);
if (sval != 0) {
kmem_free(sp, sizeof (usbkbm_save_state_t));
return (EBUSY);
}
rval = mod_remove(&modlinkage);
if (rval != 0) {
kmem_free(sp, sizeof (usbkbm_save_state_t));
space_free("SUNW,usbkbm_state");
return (rval);
}
usb_free_log_hdl(usbkbm_log_handle);
sp->usbkbm_save_led = usbkbm_led_state;
sp->usbkbm_layout = (uchar_t)usbkbm_layout;
sp->usbkbm_save_keyindex.k_abort1 = usbkbm_keyindex->k_abort1;
sp->usbkbm_save_keyindex.k_abort2 = usbkbm_keyindex->k_abort2;
sp->usbkbm_save_keyindex.k_newabort1 = usbkbm_keyindex->k_newabort1;
sp->usbkbm_save_keyindex.k_newabort2 = usbkbm_keyindex->k_newabort2;
sp->usbkbm_save_keyindex.k_normal =
kmem_alloc(USB_KEYTABLE_SIZE, KM_SLEEP);
sp->usbkbm_save_keyindex.k_shifted =
kmem_alloc(USB_KEYTABLE_SIZE, KM_SLEEP);
sp->usbkbm_save_keyindex.k_caps =
kmem_alloc(USB_KEYTABLE_SIZE, KM_SLEEP);
sp->usbkbm_save_keyindex.k_altgraph =
kmem_alloc(USB_KEYTABLE_SIZE, KM_SLEEP);
sp->usbkbm_save_keyindex.k_numlock =
kmem_alloc(USB_KEYTABLE_SIZE, KM_SLEEP);
sp->usbkbm_save_keyindex.k_control =
kmem_alloc(USB_KEYTABLE_SIZE, KM_SLEEP);
sp->usbkbm_save_keyindex.k_up =
kmem_alloc(USB_KEYTABLE_SIZE, KM_SLEEP);
bcopy(usbkbm_keyindex->k_normal,
sp->usbkbm_save_keyindex.k_normal, USB_KEYTABLE_SIZE);
bcopy(usbkbm_keyindex->k_shifted,
sp->usbkbm_save_keyindex.k_shifted, USB_KEYTABLE_SIZE);
bcopy(usbkbm_keyindex->k_caps,
sp->usbkbm_save_keyindex.k_caps, USB_KEYTABLE_SIZE);
bcopy(usbkbm_keyindex->k_altgraph,
sp->usbkbm_save_keyindex.k_altgraph, USB_KEYTABLE_SIZE);
bcopy(usbkbm_keyindex->k_numlock,
sp->usbkbm_save_keyindex.k_numlock, USB_KEYTABLE_SIZE);
bcopy(usbkbm_keyindex->k_control,
sp->usbkbm_save_keyindex.k_control, USB_KEYTABLE_SIZE);
bcopy(usbkbm_keyindex->k_up,
sp->usbkbm_save_keyindex.k_up, USB_KEYTABLE_SIZE);
kbtrans_usbkb_maptab_fini(&usbkbm_keyindex);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static struct module_info usbkbm_minfo = {
0,
"usbkbm",
0,
INFPSZ,
2048,
128
};
static struct qinit usbkbm_rinit = {
usbkbm_rput,
usbkbm_rsrv,
usbkbm_open,
usbkbm_close,
NULL,
&usbkbm_minfo
};
static struct qinit usbkbm_winit = {
usbkbm_wput,
NULL,
usbkbm_open,
usbkbm_close,
NULL,
&usbkbm_minfo
};
static struct streamtab usbkbm_info = {
&usbkbm_rinit,
&usbkbm_winit,
NULL,
NULL,
};
static int
usbkbm_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
{
usbkbm_state_t *usbkbmd;
struct iocblk mctlmsg;
mblk_t *mctl_ptr;
uintptr_t abortable = (uintptr_t)B_TRUE;
int error, ret;
if (q->q_ptr) {
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"usbkbm_open already opened");
return (0);
}
switch (sflag) {
case MODOPEN:
break;
case CLONEOPEN:
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"usbkbm_open: Clone open not supported");
default:
return (EINVAL);
}
usbkbmd = kmem_zalloc(sizeof (usbkbm_state_t), KM_SLEEP);
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"usbkbm_state= %p", (void *)usbkbmd);
usbkbmd->usbkbm_readq = q;
usbkbmd->usbkbm_writeq = WR(q);
usbkbmd->usbkbm_vkbd_type = KB_USB;
q->q_ptr = (caddr_t)usbkbmd;
WR(q)->q_ptr = (caddr_t)usbkbmd;
error = kbtrans_streams_init(q, sflag,
(struct kbtrans_hardware *)usbkbmd, &kbd_usb_callbacks,
&usbkbmd->usbkbm_kbtrans, usbkbm_led_state, 0);
if (error != 0) {
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"kbdopen: kbtrans_streams_init failed\n");
kmem_free(usbkbmd, sizeof (*usbkbmd));
return (error);
}
usbkbmd->usbkbm_polled_info.cons_polledio_version =
CONSPOLLEDIO_V1;
usbkbmd->usbkbm_polled_info.cons_polledio_argument =
(cons_polledio_arg_t)usbkbmd;
usbkbmd->usbkbm_polled_info.cons_polledio_putchar = NULL;
usbkbmd->usbkbm_polled_info.cons_polledio_getchar =
usbkbm_polled_getchar;
usbkbmd->usbkbm_polled_info.cons_polledio_ischar =
usbkbm_polled_ischar;
usbkbmd->usbkbm_polled_info.cons_polledio_enter =
usbkbm_polled_enter;
usbkbmd->usbkbm_polled_info.cons_polledio_exit =
usbkbm_polled_exit;
usbkbmd->usbkbm_polled_info.cons_polledio_setled =
(void (*)(cons_polledio_arg_t, int))usbkbm_polled_setled;
usbkbmd->usbkbm_polled_info.cons_polledio_keycheck =
(boolean_t (*)(cons_polledio_arg_t, int *,
enum keystate *))usbkbm_polled_keycheck;
usbkbmd->usbkbm_polled_buffer_head =
usbkbmd->usbkbm_polled_scancode_buffer;
usbkbmd->usbkbm_polled_buffer_tail =
usbkbmd->usbkbm_polled_scancode_buffer;
usbkbmd->usbkbm_polled_buffer_num_characters = 0;
qprocson(q);
mctlmsg.ioc_cmd = HID_GET_PARSER_HANDLE;
mctlmsg.ioc_count = 0;
mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
if (mctl_ptr == NULL) {
(void) kbtrans_streams_fini(usbkbmd->usbkbm_kbtrans);
qprocsoff(q);
kmem_free(usbkbmd, sizeof (*usbkbmd));
return (ENOMEM);
}
putnext(usbkbmd->usbkbm_writeq, mctl_ptr);
usbkbmd->usbkbm_flags |= USBKBM_QWAIT;
while (usbkbmd->usbkbm_flags & USBKBM_QWAIT) {
if (qwait_sig(q) == 0) {
usbkbmd->usbkbm_flags = 0;
(void) kbtrans_streams_fini(usbkbmd->usbkbm_kbtrans);
qprocsoff(q);
kmem_free(usbkbmd, sizeof (*usbkbmd));
return (EINTR);
}
}
if (usbkbm_get_input_format(usbkbmd) != USB_SUCCESS) {
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"usbkbm: Invalid HID Descriptor Tree."
"setting default report format");
}
if (usbkbmd->usbkbm_layout == SUN_JAPANESE_TYPE7) {
if ((ret = usbkbm_get_vid_pid(usbkbmd)) != 0) {
return (ret);
}
if ((usbkbmd->usbkbm_vid_pid.VendorId ==
HID_SUN_JAPANESE_TYPE6_KBD_VID) &&
(usbkbmd->usbkbm_vid_pid.ProductId ==
HID_SUN_JAPANESE_TYPE6_KBD_PID)) {
usbkbmd->usbkbm_layout = SUN_JAPANESE_TYPE6;
}
}
kbtrans_streams_set_keyboard(usbkbmd->usbkbm_kbtrans, KB_USB,
usbkbm_keyindex);
usbkbmd->usbkbm_flags = USBKBM_OPEN;
kbtrans_streams_enable(usbkbmd->usbkbm_kbtrans);
mctlmsg.ioc_cmd = CONSSETABORTENABLE;
mctlmsg.ioc_count = TRANSPARENT;
mctl_ptr = usba_mk_mctl(mctlmsg, &abortable, sizeof (abortable));
if (mctl_ptr != NULL) {
DB_TYPE(mctl_ptr) = M_IOCTL;
if (kbtrans_streams_message(usbkbmd->usbkbm_kbtrans, mctl_ptr)
!= KBTRANS_MESSAGE_HANDLED) {
freemsg(mctl_ptr);
}
} else {
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"usbkbm: enable abort sequence failed");
}
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"usbkbm_open exiting");
return (0);
}
static int
usbkbm_close(register queue_t *q, int flag, cred_t *crp)
{
usbkbm_state_t *usbkbmd = (usbkbm_state_t *)q->q_ptr;
(void) beeper_off();
(void) kbtrans_streams_fini(usbkbmd->usbkbm_kbtrans);
qprocsoff(q);
usbkbmd->usbkbm_flags = 0;
kmem_free(usbkbmd, sizeof (usbkbm_state_t));
USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbkbm_log_handle,
"usbkbm_close exiting");
return (0);
}
static int
usbkbm_wput(register queue_t *q, register mblk_t *mp)
{
usbkbm_state_t *usbkbmd;
enum kbtrans_message_response ret;
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_wput entering");
usbkbmd = (usbkbm_state_t *)q->q_ptr;
ret = kbtrans_streams_message(usbkbmd->usbkbm_kbtrans, mp);
if (ret == KBTRANS_MESSAGE_HANDLED) {
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_wput exiting:2");
return (0);
}
switch (mp->b_datap->db_type) {
case M_FLUSH:
if (*mp->b_rptr & FLUSHW) {
flushq(q, FLUSHDATA);
}
if (*mp->b_rptr & FLUSHR) {
flushq(RD(q), FLUSHDATA);
}
break;
case M_IOCTL:
ret = usbkbm_ioctl(q, mp);
if (ret == KBTRANS_MESSAGE_HANDLED) {
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_wput exiting:1");
return (0);
}
default:
break;
}
putnext(q, mp);
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_wput exiting:3");
return (0);
}
static enum kbtrans_message_response
usbkbm_ioctl(register queue_t *q, register mblk_t *mp)
{
usbkbm_state_t *usbkbmd;
struct iocblk mctlmsg;
struct iocblk *iocp;
mblk_t *datap, *mctl_ptr;
size_t ioctlrespsize;
int err;
int tmp;
int cycles;
int frequency;
int msecs;
char command;
err = 0;
usbkbmd = (usbkbm_state_t *)q->q_ptr;
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case CONSSETKBDTYPE:
err = miocpullup(mp, sizeof (int));
if (err != 0) {
break;
}
tmp = *(int *)mp->b_cont->b_rptr;
if (tmp != KB_PC && tmp != KB_USB) {
err = EINVAL;
break;
}
usbkbmd->usbkbm_vkbd_type = tmp;
break;
case KIOCLAYOUT:
datap = allocb(sizeof (int), BPRI_HI);
if (datap == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = usbkbmd->usbkbm_layout;
datap->b_wptr += sizeof (int);
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSLAYOUT:
if (iocp->ioc_count != TRANSPARENT) {
err = EINVAL;
break;
}
usbkbmd->usbkbm_layout = *(intptr_t *)mp->b_cont->b_rptr;
usbkbm_layout = usbkbmd->usbkbm_layout;
break;
case KIOCCMD:
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
command = (char)(*(int *)mp->b_cont->b_rptr);
if (usbkbmd->usbkbm_setled_second_byte) {
usbkbm_streams_setled((struct kbtrans_hardware *)
usbkbmd, command);
usbkbmd->usbkbm_setled_second_byte = 0;
break;
}
ioctlrespsize = 0;
err = usbkbm_kioccmd(usbkbmd, mp, command, &ioctlrespsize);
if (ioctlrespsize != 0) {
goto allocfailure;
}
break;
case CONSOPENPOLLEDIO:
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_ioctl CONSOPENPOLLEDIO");
err = miocpullup(mp, sizeof (struct cons_polledio *));
if (err != 0) {
USB_DPRINTF_L2(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_ioctl: malformed request");
break;
}
usbkbmd->usbkbm_pending_link = mp;
mctlmsg.ioc_cmd = HID_OPEN_POLLED_INPUT;
mctlmsg.ioc_count = 0;
mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
if (mctl_ptr == NULL) {
ioctlrespsize = sizeof (mctlmsg);
goto allocfailure;
}
putnext(usbkbmd->usbkbm_writeq, mctl_ptr);
return (KBTRANS_MESSAGE_HANDLED);
case CONSCLOSEPOLLEDIO:
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_ioctl CONSCLOSEPOLLEDIO mp = 0x%p", (void *)mp);
usbkbmd->usbkbm_pending_link = mp;
mctlmsg.ioc_cmd = HID_CLOSE_POLLED_INPUT;
mctlmsg.ioc_count = 0;
mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
if (mctl_ptr == NULL) {
ioctlrespsize = sizeof (mctlmsg);
goto allocfailure;
}
putnext(usbkbmd->usbkbm_writeq, mctl_ptr);
return (KBTRANS_MESSAGE_HANDLED);
case CONSSETABORTENABLE:
break;
case KIOCMKTONE:
if (iocp->ioc_count != TRANSPARENT) {
err = EINVAL;
break;
}
tmp = (int)(*(intptr_t *)mp->b_cont->b_rptr);
cycles = tmp & 0xffff;
msecs = (tmp >> 16) & 0xffff;
if (cycles == 0)
frequency = UINT16_MAX;
else if (cycles == UINT16_MAX) {
frequency = 0;
} else {
frequency = (PIT_HZ + cycles / 2) / cycles;
if (frequency > UINT16_MAX)
frequency = UINT16_MAX;
}
err = beep_mktone(frequency, msecs);
break;
default:
return (KBTRANS_MESSAGE_NOT_HANDLED);
}
if (err != 0) {
iocp->ioc_rval = 0;
iocp->ioc_error = err;
mp->b_datap->db_type = M_IOCNAK;
} else {
iocp->ioc_rval = 0;
iocp->ioc_error = 0;
mp->b_datap->db_type = M_IOCACK;
}
putnext(usbkbmd->usbkbm_readq, mp);
return (KBTRANS_MESSAGE_HANDLED);
allocfailure:
freemsg(usbkbmd->usbkbm_streams_iocpending);
usbkbmd->usbkbm_streams_iocpending = mp;
if (usbkbmd->usbkbm_streams_bufcallid) {
qunbufcall(usbkbmd->usbkbm_readq,
usbkbmd->usbkbm_streams_bufcallid);
}
usbkbmd->usbkbm_streams_bufcallid =
qbufcall(usbkbmd->usbkbm_readq, ioctlrespsize, BPRI_HI,
usbkbm_reioctl, usbkbmd);
return (KBTRANS_MESSAGE_HANDLED);
}
static int
usbkbm_kioccmd(usbkbm_state_t *usbkbmd, register mblk_t *mp,
char command, size_t *ioctlrepsize)
{
register mblk_t *datap;
register struct iocblk *iocp;
int err = 0;
iocp = (struct iocblk *)mp->b_rptr;
switch (command) {
case KBD_CMD_GETLAYOUT:
datap = allocb(sizeof (int), BPRI_HI);
if (datap == NULL) {
*ioctlrepsize = sizeof (int);
return (EIO);
}
*(int *)datap->b_wptr = usbkbmd->usbkbm_layout;
datap->b_wptr += sizeof (int);
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KBD_CMD_SETLED:
usbkbmd->usbkbm_setled_second_byte = 1;
break;
case KBD_CMD_RESET:
break;
case KBD_CMD_BELL:
(void) beeper_on(BEEP_TYPE4);
break;
case KBD_CMD_NOBELL:
(void) beeper_off();
break;
case KBD_CMD_CLICK:
case KBD_CMD_NOCLICK:
break;
default:
err = EIO;
break;
}
return (err);
}
static int
usbkbm_rput(queue_t *q, mblk_t *mp)
{
usbkbm_state_t *usbkbmd;
usbkbmd = (usbkbm_state_t *)q->q_ptr;
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_rput");
if (usbkbmd == NULL) {
freemsg(mp);
return (0);
}
switch (mp->b_datap->db_type) {
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq(WR(q), FLUSHDATA);
if (*mp->b_rptr & FLUSHR)
flushq(q, FLUSHDATA);
freemsg(mp);
return (0);
case M_BREAK:
freemsg(mp);
return (0);
case M_DATA:
if (!(usbkbmd->usbkbm_flags & USBKBM_OPEN)) {
freemsg(mp);
return (0);
}
break;
case M_CTL:
usbkbm_mctl_receive(q, mp);
return (0);
case M_ERROR:
usbkbmd->usbkbm_flags &= ~USBKBM_QWAIT;
if (*mp->b_rptr == ENODEV) {
putnext(q, mp);
} else {
freemsg(mp);
}
return (0);
case M_IOCACK:
case M_IOCNAK:
putnext(q, mp);
return (0);
default:
putnext(q, mp);
return (0);
}
if (putq(q, mp) == 0)
freemsg(mp);
return (0);
}
static int
usbkbm_rsrv(queue_t *q)
{
usbkbm_state_t *usbkbmd;
mblk_t *mp;
usbkbmd = (usbkbm_state_t *)q->q_ptr;
if (usbkbmd == NULL)
return (0);
while ((mp = getq(q)) != NULL) {
if (mp->b_datap->db_type != M_DATA) {
freemsg(mp);
continue;
}
if (MBLKL(mp) == usbkbmd->usbkbm_report_format.tlen) {
if (usbkbmd->usbkbm_report_format.keyid !=
HID_REPORT_ID_UNDEFINED) {
if (*(mp->b_rptr) !=
usbkbmd->usbkbm_report_format.keyid) {
freemsg(mp);
continue;
} else {
mp->b_rptr++;
}
}
usbkbm_unpack_usb_packet(usbkbmd,
usbkbm_streams_callback, mp->b_rptr);
}
freemsg(mp);
}
return (0);
}
static void
usbkbm_mctl_receive(register queue_t *q, register mblk_t *mp)
{
register usbkbm_state_t *usbkbmd = (usbkbm_state_t *)q->q_ptr;
register struct iocblk *iocp;
caddr_t data = NULL;
mblk_t *reply_mp;
uchar_t new_buffer[USBKBM_MAXPKTSIZE];
size_t size;
iocp = (struct iocblk *)mp->b_rptr;
if (mp->b_cont != NULL)
data = (caddr_t)mp->b_cont->b_rptr;
switch (iocp->ioc_cmd) {
case HID_SET_REPORT:
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_mctl_receive HID_SET mctl");
freemsg(mp);
break;
case HID_GET_PARSER_HANDLE:
if ((data != NULL) &&
(iocp->ioc_count == sizeof (hidparser_handle_t)) &&
(MBLKL(mp->b_cont) == iocp->ioc_count)) {
usbkbmd->usbkbm_report_descr =
*(hidparser_handle_t *)data;
} else {
usbkbmd->usbkbm_report_descr = NULL;
}
freemsg(mp);
usbkbmd->usbkbm_flags &= ~USBKBM_QWAIT;
break;
case HID_GET_VID_PID:
if ((data != NULL) &&
(iocp->ioc_count == sizeof (hid_vid_pid_t)) &&
(MBLKL(mp->b_cont) == iocp->ioc_count)) {
bcopy(data, &usbkbmd->usbkbm_vid_pid, iocp->ioc_count);
}
freemsg(mp);
usbkbmd->usbkbm_flags &= ~USBKBM_QWAIT;
break;
case HID_OPEN_POLLED_INPUT:
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_mctl_receive HID_OPEN_POLLED_INPUT");
size = sizeof (hid_polled_input_callback_t);
reply_mp = usbkbmd->usbkbm_pending_link;
if ((data != NULL) &&
(iocp->ioc_count == size) &&
(MBLKL(mp->b_cont) == size)) {
bcopy(data, &usbkbmd->usbkbm_hid_callback, size);
reply_mp->b_datap->db_type = M_IOCACK;
*(cons_polledio_t **)reply_mp->b_cont->b_rptr =
&usbkbmd->usbkbm_polled_info;
} else {
reply_mp->b_datap->db_type = M_IOCNAK;
}
freemsg(mp);
usbkbmd->usbkbm_pending_link = NULL;
putnext(q, reply_mp);
break;
case HID_CLOSE_POLLED_INPUT:
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_mctl_receive HID_CLOSE_POLLED_INPUT");
bzero(&usbkbmd->usbkbm_hid_callback,
sizeof (hid_polled_input_callback_t));
freemsg(mp);
reply_mp = usbkbmd->usbkbm_pending_link;
iocp = (struct iocblk *)reply_mp->b_rptr;
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_mctl_receive reply reply_mp 0x%p cmd 0x%x",
(void *)reply_mp, iocp->ioc_cmd);
reply_mp->b_datap->db_type = M_IOCACK;
usbkbmd->usbkbm_pending_link = NULL;
putnext(q, reply_mp);
break;
case HID_DISCONNECT_EVENT :
case HID_POWER_OFF:
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_mctl_receive HID_DISCONNECT_EVENT/HID_POWER_OFF");
bzero(new_buffer, USBKBM_MAXPKTSIZE);
usbkbm_unpack_usb_packet(usbkbmd, usbkbm_streams_callback,
new_buffer);
freemsg(mp);
break;
case HID_CONNECT_EVENT:
case HID_FULL_POWER :
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"usbkbm_mctl_receive restore LEDs");
usbkbm_streams_setled((struct kbtrans_hardware *)usbkbmd,
usbkbm_led_state);
freemsg(mp);
break;
default:
putnext(q, mp);
break;
}
}
static void
usbkbm_streams_setled(struct kbtrans_hardware *kbtrans_hw, int state)
{
struct iocblk mctlmsg;
mblk_t *mctl_ptr;
hid_req_t *LED_report;
usbkbm_state_t *usbkbmd;
uchar_t led_id, led_state;
usbkbm_led_state = (uchar_t)state;
usbkbmd = (usbkbm_state_t *)kbtrans_hw;
LED_report = kmem_zalloc(sizeof (hid_req_t), KM_NOSLEEP);
if (LED_report == NULL) {
return;
}
led_id = usbkbmd->usbkbm_report_format.keyid;
led_state = 0;
if (state & LED_NUM_LOCK) {
led_state |= USB_LED_NUM_LOCK;
}
if (state & LED_COMPOSE) {
led_state |= USB_LED_COMPOSE;
}
if (state & LED_SCROLL_LOCK) {
led_state |= USB_LED_SCROLL_LOCK;
}
if (state & LED_CAPS_LOCK) {
led_state |= USB_LED_CAPS_LOCK;
}
if (state & LED_KANA) {
led_state |= USB_LED_KANA;
}
LED_report->hid_req_version_no = HID_VERSION_V_0;
LED_report->hid_req_wValue = REPORT_TYPE_OUTPUT | led_id;
LED_report->hid_req_wLength = sizeof (uchar_t);
LED_report->hid_req_data[0] = led_state;
mctlmsg.ioc_cmd = HID_SET_REPORT;
mctlmsg.ioc_count = sizeof (LED_report);
mctl_ptr = usba_mk_mctl(mctlmsg, LED_report, sizeof (hid_req_t));
if (mctl_ptr != NULL) {
putnext(usbkbmd->usbkbm_writeq, mctl_ptr);
}
kmem_free(LED_report, sizeof (hid_req_t));
}
static boolean_t
usbkbm_polled_keycheck(struct kbtrans_hardware *hw,
int *key, enum keystate *state)
{
usbkbm_state_t *usbkbmd;
uchar_t *buffer;
unsigned size;
hid_polled_handle_t hid_polled_handle;
usbkbmd = (usbkbm_state_t *)hw;
if (usbkbmd->usbkbm_polled_buffer_num_characters != 0) {
usbkbm_get_scancode(usbkbmd, key, state);
return (B_TRUE);
}
hid_polled_handle =
usbkbmd->usbkbm_hid_callback.hid_polled_input_handle;
size = (usbkbmd->usbkbm_hid_callback.hid_polled_read)
(hid_polled_handle, &buffer);
if (size != usbkbmd->usbkbm_report_format.tlen) {
return (B_FALSE);
}
if (usbkbmd->usbkbm_report_format.keyid != HID_REPORT_ID_UNDEFINED) {
if (*buffer != usbkbmd->usbkbm_report_format.keyid) {
return (B_FALSE);
} else {
buffer++;
}
}
usbkbm_unpack_usb_packet(usbkbmd, usbkbm_poll_callback, buffer);
if (usbkbmd->usbkbm_polled_buffer_num_characters != 0) {
usbkbm_get_scancode(usbkbmd, key, state);
return (B_TRUE);
}
return (B_FALSE);
}
static ushort_t usbkbm_get_state(usbkbm_state_t *usbkbmd)
{
ushort_t ret;
ASSERT(usbkbmd->usbkbm_vkbd_type == KB_PC ||
usbkbmd->usbkbm_vkbd_type == KB_USB);
if (usbkbmd->usbkbm_vkbd_type == KB_PC)
ret = INDEXTO_PC;
else
ret = INDEXTO_USB;
return (ret);
}
static void
usbkbm_streams_callback(usbkbm_state_t *usbkbmd, int key, enum keystate state)
{
ushort_t index = usbkbm_get_state(usbkbmd);
(*usbkbm_xlate[index])(usbkbmd, key, state);
}
static void
usbkbm_wrap_kbtrans(usbkbm_state_t *usbkbmd, int key, enum keystate state)
{
kbtrans_streams_key(usbkbmd->usbkbm_kbtrans, key, state);
}
void
usbkbm_usb2pc_xlate(usbkbm_state_t *usbkbmd, int key, enum keystate state)
{
key = kbtrans_keycode_usb2pc(key);
kbtrans_streams_key(usbkbmd->usbkbm_kbtrans, key, state);
}
static void
usbkbm_poll_callback(usbkbm_state_t *usbkbmd, int key, enum keystate state)
{
if (usbkbmd->usbkbm_polled_buffer_num_characters ==
USB_POLLED_BUFFER_SIZE) {
return;
}
usbkbmd->usbkbm_polled_buffer_head->poll_key = key;
usbkbmd->usbkbm_polled_buffer_head->poll_state = state;
usbkbmd->usbkbm_polled_buffer_num_characters++;
usbkbmd->usbkbm_polled_buffer_head++;
if (usbkbmd->usbkbm_polled_buffer_head -
usbkbmd->usbkbm_polled_scancode_buffer ==
USB_POLLED_BUFFER_SIZE) {
usbkbmd->usbkbm_polled_buffer_head =
usbkbmd->usbkbm_polled_scancode_buffer;
}
}
static void
usbkbm_get_scancode(usbkbm_state_t *usbkbmd, int *key, enum keystate *state)
{
*key = usbkbmd->usbkbm_polled_buffer_tail->poll_key;
*state = usbkbmd->usbkbm_polled_buffer_tail->poll_state;
usbkbmd->usbkbm_polled_buffer_tail++;
if (usbkbmd->usbkbm_polled_buffer_tail -
usbkbmd->usbkbm_polled_scancode_buffer ==
USB_POLLED_BUFFER_SIZE) {
usbkbmd->usbkbm_polled_buffer_tail =
usbkbmd->usbkbm_polled_scancode_buffer;
}
usbkbmd->usbkbm_polled_buffer_num_characters--;
}
static void
usbkbm_polled_setled(struct kbtrans_hardware *hw, int led_state)
{
}
static int
usbkbm_polled_getchar(cons_polledio_arg_t arg)
{
usbkbm_state_t *usbkbmd;
usbkbmd = (usbkbm_state_t *)arg;
return (kbtrans_getchar(usbkbmd->usbkbm_kbtrans));
}
static boolean_t
usbkbm_polled_ischar(cons_polledio_arg_t arg)
{
usbkbm_state_t *usbkbmd;
usbkbmd = (usbkbm_state_t *)arg;
return (kbtrans_ischar(usbkbmd->usbkbm_kbtrans));
}
static void
usbkbm_polled_enter(cons_polledio_arg_t arg)
{
usbkbm_state_t *usbkbmd = (usbkbm_state_t *)arg;
hid_polled_handle_t hid_polled_handle;
int kbstart, kbend, uindex;
kbstart = usbkbmd->usbkbm_report_format.kpos;
kbend = kbstart + usbkbmd->usbkbm_report_format.klen;
for (uindex = kbstart + 2; uindex < kbend; uindex++) {
usbkbmd->usbkbm_lastusbpacket[uindex] =
usbkbmd->usbkbm_pendingusbpacket[uindex];
usbkbmd->usbkbm_pendingusbpacket[uindex] = 0;
}
hid_polled_handle =
usbkbmd->usbkbm_hid_callback.hid_polled_input_handle;
(void) (usbkbmd->usbkbm_hid_callback.hid_polled_input_enter)
(hid_polled_handle);
}
static void
usbkbm_polled_exit(cons_polledio_arg_t arg)
{
usbkbm_state_t *usbkbmd = (usbkbm_state_t *)arg;
hid_polled_handle_t hid_polled_handle;
int kbstart, kbend, uindex;
kbstart = usbkbmd->usbkbm_report_format.kpos;
kbend = kbstart + usbkbmd->usbkbm_report_format.klen;
for (uindex = kbstart + 2; uindex < kbend; uindex ++) {
usbkbmd->usbkbm_pendingusbpacket[uindex] =
usbkbmd->usbkbm_lastusbpacket[uindex];
usbkbmd->usbkbm_lastusbpacket[uindex] = 0;
}
hid_polled_handle =
usbkbmd->usbkbm_hid_callback.hid_polled_input_handle;
(void) (usbkbmd->usbkbm_hid_callback.hid_polled_input_exit)
(hid_polled_handle);
}
static void
usbkbm_unpack_usb_packet(usbkbm_state_t *usbkbmd, process_key_callback_t func,
uchar_t *usbpacket)
{
uchar_t mkb;
uchar_t lastmkb;
uchar_t *lastusbpacket = usbkbmd->usbkbm_lastusbpacket;
int packet_size, kbstart, kbend;
int uindex, lindex, rollover;
packet_size = usbkbmd->usbkbm_report_format.tlen;
kbstart = usbkbmd->usbkbm_report_format.kpos;
kbend = kbstart + usbkbmd->usbkbm_report_format.klen;
mkb = usbpacket[kbstart];
lastmkb = lastusbpacket[kbstart];
for (uindex = 0; uindex < packet_size; uindex++) {
USB_DPRINTF_L3(PRINT_MASK_PACKET, usbkbm_log_handle,
" %x ", usbpacket[uindex]);
}
USB_DPRINTF_L3(PRINT_MASK_PACKET, usbkbm_log_handle,
" is the usbkeypacket");
if (mkb != lastmkb) {
if ((mkb & USB_LSHIFTBIT) != (lastmkb & USB_LSHIFTBIT)) {
(*func)(usbkbmd, USB_LSHIFTKEY, (mkb & USB_LSHIFTBIT) ?
KEY_PRESSED : KEY_RELEASED);
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"unpack: sending USB_LSHIFTKEY");
}
if ((mkb & USB_LCTLBIT) != (lastmkb & USB_LCTLBIT)) {
(*func)(usbkbmd, USB_LCTLCKEY, mkb & USB_LCTLBIT ?
KEY_PRESSED : KEY_RELEASED);
}
if ((mkb & USB_LALTBIT) != (lastmkb & USB_LALTBIT)) {
(*func)(usbkbmd, USB_LALTKEY, mkb & USB_LALTBIT ?
KEY_PRESSED : KEY_RELEASED);
}
if ((mkb & USB_LMETABIT) != (lastmkb & USB_LMETABIT)) {
(*func)(usbkbmd, USB_LMETAKEY, mkb & USB_LMETABIT ?
KEY_PRESSED : KEY_RELEASED);
}
if ((mkb & USB_RMETABIT) != (lastmkb & USB_RMETABIT)) {
(*func)(usbkbmd, USB_RMETAKEY, mkb & USB_RMETABIT ?
KEY_PRESSED : KEY_RELEASED);
}
if ((mkb & USB_RALTBIT) != (lastmkb & USB_RALTBIT)) {
(*func)(usbkbmd, USB_RALTKEY, mkb & USB_RALTBIT ?
KEY_PRESSED : KEY_RELEASED);
}
if ((mkb & USB_RCTLBIT) != (lastmkb & USB_RCTLBIT)) {
(*func)(usbkbmd, USB_RCTLCKEY, mkb & USB_RCTLBIT ?
KEY_PRESSED : KEY_RELEASED);
}
if ((mkb & USB_RSHIFTBIT) != (lastmkb & USB_RSHIFTBIT)) {
(*func)(usbkbmd, USB_RSHIFTKEY, mkb & USB_RSHIFTBIT ?
KEY_PRESSED : KEY_RELEASED);
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"unpack: sending USB_RSHIFTKEY");
}
}
lastusbpacket[kbstart] = usbpacket[kbstart];
if (usbpacket[kbstart + 2] == USB_ERRORROLLOVER) {
rollover = 1;
for (uindex = kbstart + 3; uindex < kbend;
uindex++) {
if (usbpacket[uindex] != USB_ERRORROLLOVER) {
rollover = 0;
break;
}
}
if (rollover) {
USB_DPRINTF_L3(PRINT_MASK_ALL, usbkbm_log_handle,
"unpack: errorrollover");
return;
}
}
for (lindex = kbstart + 2; lindex < kbend; lindex++) {
int released = 1;
if (lastusbpacket[lindex] == 0) {
continue;
}
for (uindex = kbstart + 2; uindex < kbend; uindex++)
if (usbpacket[uindex] == lastusbpacket[lindex]) {
released = 0;
break;
}
if (released) {
(*func)(usbkbmd, lastusbpacket[lindex], KEY_RELEASED);
}
}
for (uindex = kbstart + 2; uindex < kbend; uindex++) {
int newkey = 1;
usbkbmd->usbkbm_pendingusbpacket[uindex] = usbpacket[uindex];
if (usbpacket[uindex] == 0) {
continue;
}
for (lindex = kbstart + 2; lindex < kbend; lindex++) {
if (usbpacket[uindex] == lastusbpacket[lindex]) {
newkey = 0;
break;
}
}
if (newkey) {
if (!usbkbm_is_modkey(usbpacket[uindex])) {
(*func)(usbkbmd, usbpacket[uindex],
KEY_PRESSED);
} else {
usbkbmd->usbkbm_pendingusbpacket[uindex] = 0;
continue;
}
}
}
for (uindex = kbstart + 2; uindex < kbend; uindex++) {
lastusbpacket[uindex] =
usbkbmd->usbkbm_pendingusbpacket[uindex];
usbkbmd->usbkbm_pendingusbpacket[uindex] = 0;
}
}
static boolean_t
usbkbm_is_modkey(uchar_t key)
{
switch (key) {
case USB_LSHIFTKEY:
case USB_LCTLCKEY:
case USB_LALTKEY:
case USB_LMETAKEY:
case USB_RCTLCKEY:
case USB_RSHIFTKEY:
case USB_RMETAKEY:
case USB_RALTKEY:
return (B_TRUE);
default:
break;
}
return (B_FALSE);
}
static void
usbkbm_reioctl(void *arg)
{
usbkbm_state_t *usbkbmd;
mblk_t *mp;
usbkbmd = (usbkbm_state_t *)arg;
usbkbmd->usbkbm_streams_bufcallid = 0;
if ((mp = usbkbmd->usbkbm_streams_iocpending) != NULL) {
usbkbmd->usbkbm_streams_iocpending = NULL;
(void) usbkbm_ioctl(usbkbmd->usbkbm_writeq, mp);
}
}
static int
usbkbm_get_vid_pid(usbkbm_state_t *usbkbmd)
{
struct iocblk mctlmsg;
mblk_t *mctl_ptr;
queue_t *q = usbkbmd->usbkbm_readq;
mctlmsg.ioc_cmd = HID_GET_VID_PID;
mctlmsg.ioc_count = 0;
mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
if (mctl_ptr == NULL) {
(void) kbtrans_streams_fini(usbkbmd->usbkbm_kbtrans);
qprocsoff(q);
kmem_free(usbkbmd, sizeof (usbkbm_state_t));
return (ENOMEM);
}
putnext(usbkbmd->usbkbm_writeq, mctl_ptr);
usbkbmd->usbkbm_flags |= USBKBM_QWAIT;
while (usbkbmd->usbkbm_flags & USBKBM_QWAIT) {
if (qwait_sig(q) == 0) {
usbkbmd->usbkbm_flags = 0;
(void) kbtrans_streams_fini(usbkbmd->usbkbm_kbtrans);
qprocsoff(q);
kmem_free(usbkbmd, sizeof (usbkbm_state_t));
return (EINTR);
}
}
return (0);
}
static int
usbkbm_get_input_format(usbkbm_state_t *usbkbmd)
{
hidparser_rpt_t *kb_rpt;
uint_t i, kbd_page = 0, kpos = 0, klen = 0, limit = 0;
uint32_t rptcnt, rptsz;
usbkbm_report_format_t *kbd_fmt = &usbkbmd->usbkbm_report_format;
int rptid, rval;
kbd_fmt->keyid = HID_REPORT_ID_UNDEFINED;
kbd_fmt->tlen = USB_KBD_BOOT_PROTOCOL_PACKET_SIZE;
kbd_fmt->klen = kbd_fmt->tlen;
kbd_fmt->kpos = 0;
if (usbkbmd->usbkbm_report_descr == NULL) {
return (USB_FAILURE);
}
if (hidparser_get_country_code(usbkbmd->usbkbm_report_descr,
(uint16_t *)&usbkbmd->usbkbm_layout) == HIDPARSER_FAILURE) {
USB_DPRINTF_L3(PRINT_MASK_OPEN,
usbkbm_log_handle, "get_country_code failed"
"setting default layout(0)");
usbkbmd->usbkbm_layout = usbkbm_layout;
}
if (hidparser_get_usage_attribute(
usbkbmd->usbkbm_report_descr,
0,
HIDPARSER_ITEM_INPUT,
HID_KEYBOARD_KEYPAD_KEYS,
0,
HIDPARSER_ITEM_REPORT_ID,
&rptid) == HIDPARSER_NOT_FOUND) {
return (USB_SUCCESS);
}
kb_rpt = kmem_zalloc(sizeof (hidparser_rpt_t), KM_SLEEP);
rval = hidparser_get_usage_list_in_order(
usbkbmd->usbkbm_report_descr,
rptid,
HIDPARSER_ITEM_INPUT,
kb_rpt);
if (rval != HIDPARSER_SUCCESS) {
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"get_usage_list_in_order failed");
kmem_free(kb_rpt, sizeof (hidparser_rpt_t));
return (USB_FAILURE);
}
for (i = 0; i < kb_rpt->no_of_usages; i++) {
rptcnt = kb_rpt->usage_descr[i].rptcnt;
rptsz = kb_rpt->usage_descr[i].rptsz;
switch (kb_rpt->usage_descr[i].usage_page) {
case HID_KEYBOARD_KEYPAD_KEYS:
if (!kbd_page) {
kpos = limit;
kbd_page = 1;
}
klen += rptcnt * rptsz;
default:
limit += rptcnt * rptsz;
break;
}
}
kmem_free(kb_rpt, sizeof (hidparser_rpt_t));
if (!kbd_page || limit > USBKBM_MAXPKTSIZE * 8 ||
kpos + klen > limit || (kpos % 8 != 0)) {
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbkbm_log_handle,
"Invalid input report format: kbd_page (%d), limit (%d), "
"kpos (%d), klen (%d)", kbd_page, limit, kpos, klen);
return (USB_FAILURE);
}
kbd_fmt->keyid = (uint8_t)rptid;
kbd_fmt->tlen = limit / 8 + 1;
kbd_fmt->klen = klen / 8;
kbd_fmt->kpos = kpos / 8;
return (USB_SUCCESS);
}