usbms.c revision d291d9f21e8c0417aec99de243dd48bc400002d0
/*
* 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"
#include <sys/usb/usba/usbai_version.h>
#include <sys/usb/usba.h>
#include <sys/usb/clients/hid/hid.h>
#include <sys/usb/clients/hidparser/hidparser.h>
#include <sys/stropts.h>
#include <sys/strsun.h>
#include <sys/vuid_event.h>
#include <sys/vuid_wheel.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/strtty.h>
#include <sys/msreg.h>
#include <sys/msio.h>
#include <sys/usb/clients/usbms/usbms.h>
/* debugging information */
uint_t usbms_errmask = (uint_t)PRINT_MASK_ALL;
uint_t usbms_errlevel = USB_LOG_L2;
static usb_log_handle_t usbms_log_handle;
static struct streamtab usbms_streamtab;
static struct fmodsw fsw = {
"usbms",
&usbms_streamtab,
D_MP | D_MTPERMOD
};
/*
* Module linkage information for the kernel.
*/
static struct modlstrmod modlstrmod = {
&mod_strmodops,
"USB mouse streams %I%",
&fsw
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modlstrmod,
NULL
};
int
_init(void)
{
int rval = mod_install(&modlinkage);
if (rval == 0) {
usbms_log_handle = usb_alloc_log_hdl(NULL, "usbms",
&usbms_errlevel, &usbms_errmask, NULL, 0);
}
return (rval);
}
int
_fini(void)
{
int rval = mod_remove(&modlinkage);
if (rval == 0) {
usb_free_log_hdl(usbms_log_handle);
}
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/* Function prototypes */
static void usbms_reioctl(void *);
static void usbms_ioctl(queue_t *, mblk_t *);
static int usbms_open();
static int usbms_close();
static int usbms_wput();
static void usbms_rput();
static void usbms_mctl_receive(
register queue_t *q,
register mblk_t *mp);
static void usbms_rserv(queue_t *q);
static void usbms_miocdata(
register queue_t *q,
register mblk_t *mp);
static void usbms_resched(void *);
static int usbms_getparms(
register Ms_parms *data,
usbms_state_t *usbmsp);
static int usbms_setparms(
register Ms_parms *data,
usbms_state_t *usbmsp);
static int usbms_get_screen_parms(
register queue_t *q,
register mblk_t *datap);
static void usbms_flush(usbms_state_t *usbmsp);
static void usbms_incr(void *);
static void usbms_input(
usbms_state_t *usbmsp,
mblk_t *mp);
static void usbms_rserv_vuid_button(
queue_t *q,
struct usbmouseinfo *mi,
mblk_t **bpaddr);
static void usbms_rserv_vuid_event_y(
queue_t *q,
struct usbmouseinfo *mi,
mblk_t **bpaddr);
static void usbms_rserv_vuid_event_x(
queue_t *q,
struct usbmouseinfo *mi,
mblk_t **bpaddr);
static void usbms_rserv_vuid_event_wheel(
queue_t *,
struct usbmouseinfo *,
mblk_t **,
ushort_t id);
static int usbms_check_for_wheels(usbms_state_t *);
static int usbms_make_copyreq(
mblk_t *,
uint_t pvtsize,
uint_t state,
uint_t reqsize,
uint_t contsize,
uint_t copytype);
static int usbms_service_wheel_info(
queue_t *,
mblk_t *);
static int usbms_service_wheel_state(
queue_t *,
mblk_t *,
uint_t cmd);
static void usbms_ack_ioctl(mblk_t *);
static int usbms_read_input_data_format(usbms_state_t *);
extern void uniqtime32();
/*
* Device driver qinit functions
*/
static struct module_info usbms_mod_info = {
0x0ffff, /* module id number */
"usbms", /* module name */
0, /* min packet size accepted */
INFPSZ, /* max packet size accepted */
512, /* hi-water mark */
128 /* lo-water mark */
};
/* read side queue information structure */
static struct qinit rinit = {
(int (*)())usbms_rput, /* put procedure not needed */
(int (*)())usbms_rserv, /* service procedure */
usbms_open, /* called on startup */
usbms_close, /* called on finish */
NULL, /* for future use */
&usbms_mod_info, /* module information structure */
NULL /* module statistics structure */
};
/* write side queue information structure */
static struct qinit winit = {
usbms_wput, /* put procedure */
NULL, /* no service proecedure needed */
NULL, /* open not used on write side */
NULL, /* close not used on write side */
NULL, /* for future use */
&usbms_mod_info, /* module information structure */
NULL /* module statistics structure */
};
static struct streamtab usbms_streamtab = {
&rinit,
&winit,
NULL, /* not a MUX */
NULL /* not a MUX */
};
/*
* Message when overrun circular buffer
*/
static int overrun_msg;
/* Increment when overrun circular buffer */
static int overrun_cnt;
extern int hz;
/*
* Mouse buffer size in bytes. Place here as variable so that one could
* massage it using adb if it turns out to be too small.
*/
static uint16_t usbms_buf_bytes = USBMS_BUF_BYTES;
/*
* Regular STREAMS Entry points
*/
/*
* usbms_open() :
* open() entry point for the USB mouse module.
*/
/*ARGSUSED*/
static int
usbms_open(queue_t *q,
dev_t *devp,
int flag,
int sflag,
cred_t *credp)
{
register struct usbmousebuf *mousebufp;
register struct ms_softc *msd_soft;
usbms_state_t *usbmsp;
struct iocblk mctlmsg;
mblk_t *mctl_ptr;
/* Clone opens are not allowed */
if (sflag != MODOPEN)
return (EINVAL);
/* If the module is already open, just return */
if (q->q_ptr) {
return (0);
}
/* allocate usbms state structure */
usbmsp = kmem_zalloc(sizeof (usbms_state_t), KM_SLEEP);
q->q_ptr = usbmsp;
WR(q)->q_ptr = usbmsp;
usbmsp->usbms_rq_ptr = q;
usbmsp->usbms_wq_ptr = WR(q);
qprocson(q);
/*
* Set up private data.
*/
usbmsp->usbms_state = USBMS_WAIT_BUTN;
usbmsp->usbms_iocpending = NULL;
usbmsp->usbms_jitter_thresh = USBMS_JITTER_THRESH;
usbmsp->usbms_speedlimit = USBMS_SPEEDLIMIT;
usbmsp->usbms_speedlaw = USBMS_SPEEDLAW;
usbmsp->usbms_speed_count = USBMS_SPEED_COUNT;
msd_soft = &usbmsp->usbms_softc;
/*
* Initially set the format to MS_VUID_FORMAT
*/
msd_soft->ms_readformat = MS_VUID_FORMAT;
/*
* Allocate buffer and initialize data.
*/
msd_soft->ms_bufbytes = usbms_buf_bytes;
mousebufp = kmem_zalloc((uint_t)msd_soft->ms_bufbytes,
KM_SLEEP);
/* Truncation will happen */
mousebufp->mb_size = (uint16_t)((msd_soft->ms_bufbytes -
sizeof (struct usbmousebuf)) /
sizeof (struct usbmouseinfo));
mousebufp->mb_info = (struct usbmouseinfo *)((char *)mousebufp +
sizeof (struct usbmousebuf));
usbmsp->usbms_buf = mousebufp;
msd_soft->ms_vuidaddr = VKEY_FIRST;
usbmsp->usbms_jittertimeout = JITTER_TIMEOUT;
/* request hid report descriptor from HID */
mctlmsg.ioc_cmd = HID_GET_PARSER_HANDLE;
mctlmsg.ioc_count = 0;
mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
if (mctl_ptr == NULL) {
qprocsoff(q);
kmem_free(usbmsp->usbms_buf, msd_soft->ms_bufbytes);
kmem_free(usbmsp, sizeof (usbms_state_t));
return (ENOMEM);
}
usbmsp->usbms_flags |= USBMS_QWAIT;
putnext(usbmsp->usbms_wq_ptr, mctl_ptr);
/*
* Now that signal has been sent, wait for report descriptor. Cleanup
* if user signals in the mean time (as when this gets opened in an
* inappropriate context and the user types a ^C).
*/
while (usbmsp->usbms_flags & USBMS_QWAIT) {
if (qwait_sig(q) == 0) {
qprocsoff(q);
kmem_free(usbmsp->usbms_buf, msd_soft->ms_bufbytes);
kmem_free(usbmsp, sizeof (usbms_state_t));
return (EINTR);
}
}
if (usbmsp->usbms_report_descr_handle != NULL) {
if (hidparser_get_usage_attribute(
usbmsp->usbms_report_descr_handle,
0,
HIDPARSER_ITEM_INPUT,
USBMS_USAGE_PAGE_BUTTON,
0,
HIDPARSER_ITEM_REPORT_COUNT,
(int32_t *)&usbmsp->usbms_num_buttons) ==
HIDPARSER_SUCCESS) {
USB_DPRINTF_L2(PRINT_MASK_ALL,
usbms_log_handle, "Num of buttons is : %d",
usbmsp->usbms_num_buttons);
} else {
USB_DPRINTF_L3(PRINT_MASK_OPEN,
usbms_log_handle,
"hidparser_get_usage_attribute failed : "
"Set to default number of buttons(3).");
usbmsp->usbms_num_buttons = USB_MS_DEFAULT_BUTTON_NO;
}
} else {
USB_DPRINTF_L1(PRINT_MASK_ALL,
usbms_log_handle, "Invalid HID "
"Descriptor Tree. Set to default value(3 buttons).");
usbmsp->usbms_num_buttons = USB_MS_DEFAULT_BUTTON_NO;
}
/* check if this mouse has wheel */
if (usbms_check_for_wheels(usbmsp) == USB_FAILURE) {
USB_DPRINTF_L2(PRINT_MASK_ALL, usbms_log_handle,
"No wheels detected");
} else {
USB_DPRINTF_L2(PRINT_MASK_ALL, usbms_log_handle,
"Wheel detected");
}
usbms_flush(usbmsp);
/* get the data format from the hid descriptor */
if (usbms_read_input_data_format(usbmsp) != USB_SUCCESS) {
qprocsoff(q);
kmem_free(usbmsp->usbms_buf, msd_soft->ms_bufbytes);
kmem_free(usbmsp, sizeof (usbms_state_t));
return (EINVAL);
}
usbmsp->usbms_flags |= USBMS_OPEN;
USB_DPRINTF_L3(PRINT_MASK_OPEN, usbms_log_handle,
"usbms_open exiting");
return (0);
}
/*
* usbms_close() :
* close() entry point for the USB mouse module.
*/
/*ARGSUSED*/
static int
usbms_close(queue_t *q,
int flag,
cred_t *credp)
{
usbms_state_t *usbmsp = q->q_ptr;
register struct ms_softc *ms = &usbmsp->usbms_softc;
USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbms_log_handle,
"usbms_close entering");
qprocsoff(q);
if (usbmsp->usbms_jitter) {
(void) quntimeout(q,
(timeout_id_t)(long)usbmsp->usbms_timeout_id);
usbmsp->usbms_jitter = 0;
}
if (usbmsp->usbms_reioctl_id) {
qunbufcall(q, (bufcall_id_t)(long)usbmsp->usbms_reioctl_id);
usbmsp->usbms_reioctl_id = 0;
}
if (usbmsp->usbms_resched_id) {
qunbufcall(q, (bufcall_id_t)usbmsp->usbms_resched_id);
usbmsp->usbms_resched_id = 0;
}
if (usbmsp->usbms_iocpending != NULL) {
/*
* We were holding an "ioctl" response pending the
* availability of an "mblk" to hold data to be passed up;
* another "ioctl" came through, which means that "ioctl"
* must have timed out or been aborted.
*/
freemsg(usbmsp->usbms_iocpending);
usbmsp->usbms_iocpending = NULL;
}
/* Free mouse buffer */
if (usbmsp->usbms_buf != NULL) {
kmem_free(usbmsp->usbms_buf, ms->ms_bufbytes);
}
kmem_free(usbmsp, sizeof (usbms_state_t));
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
USB_DPRINTF_L3(PRINT_MASK_CLOSE, usbms_log_handle,
"usbms_close exiting");
return (0);
}
/*
* usbms_rserv() :
* Read queue service routine.
* Turn buffered mouse events into stream messages.
*/
static void
usbms_rserv(queue_t *q)
{
usbms_state_t *usbmsp = q->q_ptr;
struct ms_softc *ms;
struct usbmousebuf *b;
struct usbmouseinfo *mi;
mblk_t *bp;
ushort_t i, loop;
uchar_t nbutt = (uchar_t)usbmsp->usbms_num_buttons;
ms = &usbmsp->usbms_softc;
b = usbmsp->usbms_buf;
USB_DPRINTF_L3(PRINT_MASK_SERV, usbms_log_handle,
"usbms_rserv entering");
while (canputnext(q) && ms->ms_oldoff != b->mb_off) {
mi = &b->mb_info[ms->ms_oldoff];
switch (ms->ms_readformat) {
case MS_3BYTE_FORMAT: {
register char *cp;
if ((usbmsp->usbms_idf).xlen != 1) {
USB_DPRINTF_L3(PRINT_MASK_SERV,
usbms_log_handle,
"Can't set to 3 byte format. Length != 1");
return;
}
if ((bp = allocb(3, BPRI_HI)) != NULL) {
cp = (char *)bp->b_wptr;
*cp++ = 0x80 | (mi->mi_buttons & 0xFF);
/* Update read buttons */
ms->ms_prevbuttons = mi->mi_buttons;
*cp++ = (mi->mi_x & 0xFF);
*cp++ = ((-mi->mi_y) & 0xFF);
/* lower pri to avoid mouse droppings */
bp->b_wptr = (uchar_t *)cp;
putnext(q, bp);
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
(bufcall_id_t)usbmsp->usbms_resched_id);
}
usbmsp->usbms_resched_id = qbufcall(q,
(size_t)3,
(uint_t)BPRI_HI,
(void (*)())usbms_resched,
(void *) usbmsp);
if (usbmsp->usbms_resched_id == 0)
return; /* try again later */
/* bufcall failed; just pitch this event */
/* or maybe flush queue? */
}
ms->ms_oldoff++; /* next event */
/* circular buffer wraparound */
if (ms->ms_oldoff >= b->mb_size) {
ms->ms_oldoff = 0;
}
break;
}
case MS_VUID_FORMAT:
default: {
do {
bp = NULL;
switch (ms->ms_eventstate) {
case EVENT_WHEEL:
loop = (usbmsp->usbms_num_wheels ?
1 : 0);
if (usbmsp->usbms_num_wheels) {
for (i = 0; i < loop; i++) {
usbms_rserv_vuid_event_wheel
(q, mi, &bp, i);
}
}
break;
case EVENT_BUT8:
case EVENT_BUT7:
case EVENT_BUT6:
case EVENT_BUT5:
case EVENT_BUT4:
case EVENT_BUT3: /* Send right button */
case EVENT_BUT2: /* Send middle button */
case EVENT_BUT1: /* Send left button */
usbms_rserv_vuid_button(q, mi, &bp);
break;
case EVENT_Y:
usbms_rserv_vuid_event_y(q, mi, &bp);
break;
case EVENT_X:
usbms_rserv_vuid_event_x(q, mi, &bp);
break;
default:
/* start again */
ms->ms_eventstate = EVENT_WHEEL;
break;
}
if (bp != NULL) {
/* lower pri to avoid mouse droppings */
bp->b_wptr += sizeof (Firm_event);
putnext(q, bp);
}
if (ms->ms_eventstate == EVENT_X) {
ms->ms_eventstate = EVENT_WHEEL;
} else if (ms->ms_eventstate == EVENT_WHEEL) {
ms->ms_oldoff++; /* next event */
/* circular buffer wraparound */
if (ms->ms_oldoff >= b->mb_size) {
ms->ms_oldoff = 0;
}
ms->ms_eventstate = EVENT_BUT(nbutt);
} else
ms->ms_eventstate--;
} while (ms->ms_eventstate != EVENT_BUT(nbutt));
}
}
}
USB_DPRINTF_L3(PRINT_MASK_SERV, usbms_log_handle,
"usbms_rserv exiting");
}
/*
* usbms_rserv_vuid_event_wheel
* convert wheel data to firm events
*/
static void
usbms_rserv_vuid_event_wheel(queue_t *q,
struct usbmouseinfo *mi,
mblk_t **bpaddr,
ushort_t id)
{
Firm_event *fep;
mblk_t *tmp;
struct ms_softc *ms;
usbms_state_t *usbmsp = (usbms_state_t *)q->q_ptr;
if (!(usbmsp->usbms_wheel_state_bf & (1 << id))) {
return;
}
ms = &usbmsp->usbms_softc;
if (mi->mi_z) {
if ((tmp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
fep = (Firm_event *)tmp->b_wptr;
fep->id = vuid_id_addr(vuid_first(VUID_WHEEL)) |
vuid_id_offset(id);
fep->pair_type = FE_PAIR_NONE;
fep->pair = NULL;
fep->value = mi->mi_z;
fep->time = mi->mi_time;
*bpaddr = tmp;
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
(bufcall_id_t)usbmsp->usbms_resched_id);
}
usbmsp->usbms_resched_id =
qbufcall(q, sizeof (Firm_event), BPRI_HI,
(void (*)())usbms_resched, (void *) usbmsp);
if (usbmsp->usbms_resched_id == 0) {
/* try again later */
return;
}
/* flush the queue */
ms->ms_eventstate = EVENT_WHEEL;
}
}
}
/*
* usbms_rserv_vuid_button() :
* Process a VUID button event
*/
static void
usbms_rserv_vuid_button(queue_t *q,
struct usbmouseinfo *mi,
mblk_t **bpaddr)
{
usbms_state_t *usbmsp = q->q_ptr;
struct ms_softc *ms;
int button_number;
uchar_t hwbit = 0x0;
Firm_event *fep;
mblk_t *bp;
uchar_t nbutt;
ms = &usbmsp->usbms_softc;
/* Test button. Send an event if it changed. */
nbutt = (uchar_t)usbmsp->usbms_num_buttons;
button_number = nbutt - (EVENT_BUT(nbutt) - ms->ms_eventstate) - 1;
switch (button_number) {
case 2:
/* Right button */
hwbit = 0x01;
break;
case 1:
/* Middle button */
hwbit = 0x02;
break;
case 0:
/* Left button */
hwbit = 0x04;
break;
default :
/* Any other button */
hwbit = USBMS_BUT(nbutt) >> (EVENT_BUT(nbutt) -
ms->ms_eventstate);
break;
}
if ((ms->ms_prevbuttons & hwbit) !=
(mi->mi_buttons & hwbit)) {
if ((bp = allocb(sizeof (Firm_event),
BPRI_HI)) != NULL) {
*bpaddr = bp;
fep = (Firm_event *)bp->b_wptr;
fep->id = vuid_id_addr(
ms->ms_vuidaddr) |
vuid_id_offset(BUT(1)
+ button_number);
fep->pair_type = FE_PAIR_NONE;
fep->pair = 0;
/*
* Update read buttons and set
* value
*/
if (mi->mi_buttons & hwbit) {
fep->value = 0;
ms->ms_prevbuttons |=
hwbit;
} else {
fep->value = 1;
ms->ms_prevbuttons &=
~hwbit;
}
fep->time = mi->mi_time;
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
(bufcall_id_t)usbmsp->usbms_resched_id);
}
usbmsp->usbms_resched_id =
qbufcall(q,
sizeof (Firm_event),
BPRI_HI,
(void (*)())usbms_resched,
(void *) usbmsp);
if (usbmsp->usbms_resched_id == 0)
/* try again later */
return;
/*
* bufcall failed; just pitch
* this event
*/
/* or maybe flush queue? */
ms->ms_eventstate = EVENT_WHEEL;
}
}
}
/*
* usbms_rserv_vuid_event_y() :
* Process a VUID y-event
*/
static void
usbms_rserv_vuid_event_y(register queue_t *q,
register struct usbmouseinfo *mi,
mblk_t **bpaddr)
{
usbms_state_t *usbmsp = q->q_ptr;
register struct ms_softc *ms;
register Firm_event *fep;
mblk_t *bp;
ms = &usbmsp->usbms_softc;
/*
* The (max, 0) message and (0, max) message are always sent before
* the button click message is sent on the IBM Bladecenter. Stop
* their sending may prevent the coordinate from moving to the
* (max, max).
*/
if (!(((usbmsp->usbms_idf).yattr) & HID_MAIN_ITEM_RELATIVE)) {
if ((mi->mi_x == 0) &&
(mi->mi_y == usbmsp->usbms_logical_Ymax)) {
return;
}
}
/* Send y if changed. */
if (mi->mi_y != 0) {
if ((bp = allocb(sizeof (Firm_event),
BPRI_HI)) != NULL) {
*bpaddr = bp;
fep = (Firm_event *)bp->b_wptr;
if (((usbmsp->usbms_idf).yattr) &
HID_MAIN_ITEM_RELATIVE) {
fep->id = vuid_id_addr(
ms->ms_vuidaddr) |
vuid_id_offset(
LOC_Y_DELTA);
fep->pair_type =
FE_PAIR_ABSOLUTE;
fep->pair =
(uchar_t)LOC_Y_ABSOLUTE;
fep->value = -(mi->mi_y);
} else {
fep->id = vuid_id_addr(
ms->ms_vuidaddr) |
vuid_id_offset(
LOC_Y_ABSOLUTE);
fep->pair_type = FE_PAIR_DELTA;
fep->pair = (uchar_t)LOC_Y_DELTA;
fep->value = (mi->mi_y *
((usbmsp->usbms_resolution).height) /
usbmsp->usbms_logical_Ymax);
if ((mi->mi_y *
((usbmsp->usbms_resolution).height) %
usbmsp->usbms_logical_Ymax) >=
(usbmsp->usbms_logical_Ymax / 2)) {
fep->value ++;
}
}
fep->time = mi->mi_time;
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
(bufcall_id_t)usbmsp->usbms_resched_id);
}
usbmsp->usbms_resched_id =
qbufcall(q,
sizeof (Firm_event),
BPRI_HI,
(void (*)())usbms_resched,
(void *)usbmsp);
if (usbmsp->usbms_resched_id == 0) {
/* try again later */
return;
}
/*
* bufcall failed; just pitch
* this event
*/
/* or maybe flush queue? */
ms->ms_eventstate = EVENT_WHEEL;
}
}
}
/*
* usbms_rserv_vuid_event_x() :
* Process a VUID x-event
*/
static void
usbms_rserv_vuid_event_x(register queue_t *q,
register struct usbmouseinfo *mi,
mblk_t **bpaddr)
{
usbms_state_t *usbmsp = q->q_ptr;
register struct ms_softc *ms;
register Firm_event *fep;
mblk_t *bp;
ms = &usbmsp->usbms_softc;
/*
* The (max, 0) message and (0, max) message are always sent before
* the button click message is sent on the IBM Bladecenter. Stop
* their sending may prevent the coordinate from moving to the
* (max, max).
*/
if (!(((usbmsp->usbms_idf).xattr) & HID_MAIN_ITEM_RELATIVE)) {
if ((mi->mi_y == 0) &&
(mi->mi_x == usbmsp->usbms_logical_Xmax)) {
return;
}
}
/* Send x if changed. */
if (mi->mi_x != 0) {
if ((bp = allocb(sizeof (Firm_event),
BPRI_HI)) != NULL) {
*bpaddr = bp;
fep = (Firm_event *)bp->b_wptr;
if (((usbmsp->usbms_idf).xattr) &
HID_MAIN_ITEM_RELATIVE) {
fep->id = vuid_id_addr(
ms->ms_vuidaddr) |
vuid_id_offset(LOC_X_DELTA);
fep->pair_type =
FE_PAIR_ABSOLUTE;
fep->pair =
(uchar_t)LOC_X_ABSOLUTE;
fep->value = mi->mi_x;
} else {
fep->id = vuid_id_addr(ms->ms_vuidaddr) |
vuid_id_offset(LOC_X_ABSOLUTE);
fep->pair_type = FE_PAIR_DELTA;
fep->pair = (uchar_t)LOC_X_DELTA;
fep->value = (mi->mi_x *
((usbmsp->usbms_resolution).width) /
usbmsp->usbms_logical_Xmax);
if ((mi->mi_x *
((usbmsp->usbms_resolution).width) %
usbmsp->usbms_logical_Xmax) >=
(usbmsp->usbms_logical_Xmax / 2)) {
fep->value ++;
}
}
fep->time = mi->mi_time;
} else {
if (usbmsp->usbms_resched_id)
qunbufcall(q,
(bufcall_id_t)usbmsp->usbms_resched_id);
usbmsp->usbms_resched_id =
qbufcall(q,
sizeof (Firm_event),
BPRI_HI,
(void (*)())usbms_resched,
(void *) usbmsp);
if (usbmsp->usbms_resched_id == 0)
/* try again later */
return;
/*
* bufcall failed; just
* pitch this event
*/
/* or maybe flush queue? */
ms->ms_eventstate = EVENT_WHEEL;
}
}
}
/*
* usbms_resched() :
* Callback routine for the qbufcall() in case
* of allocb() failure. When buffer becomes
* available, this function is called and
* enables the queue.
*/
static void
usbms_resched(void * usbmsp)
{
register queue_t *q;
register usbms_state_t *tmp_usbmsp = (usbms_state_t *)usbmsp;
tmp_usbmsp->usbms_resched_id = 0;
if ((q = tmp_usbmsp->usbms_rq_ptr) != 0)
qenable(q); /* run the service procedure */
}
/*
* usbms_wput() :
* wput() routine for the mouse module.
* Module below : hid, module above : consms
*/
static int
usbms_wput(queue_t *q,
mblk_t *mp)
{
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"usbms_wput entering");
switch (mp->b_datap->db_type) {
case M_FLUSH: /* Canonical flush handling */
if (*mp->b_rptr & FLUSHW) {
flushq(q, FLUSHDATA);
}
if (*mp->b_rptr & FLUSHR) {
flushq(RD(q), FLUSHDATA);
}
putnext(q, mp); /* pass it down the line. */
break;
case M_IOCTL:
usbms_ioctl(q, mp);
break;
case M_IOCDATA:
usbms_miocdata(q, mp);
break;
default:
putnext(q, mp); /* pass it down the line. */
}
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"usbms_wput exiting");
return (0);
}
/*
* usbms_ioctl() :
* Process ioctls we recognize and own. Otherwise, NAK.
*/
static void
usbms_ioctl(register queue_t *q,
register mblk_t *mp)
{
usbms_state_t *usbmsp = (usbms_state_t *)q->q_ptr;
register struct ms_softc *ms;
register struct iocblk *iocp;
Vuid_addr_probe *addr_probe;
uint_t ioctlrespsize;
int err = 0;
mblk_t *datap;
ushort_t transparent = 0;
USB_DPRINTF_L3(PRINT_MASK_IOCTL, usbms_log_handle,
"usbms_ioctl entering");
if (usbmsp == NULL) {
miocnak(q, mp, 0, EINVAL);
return;
}
ms = &usbmsp->usbms_softc;
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case VUIDSFORMAT:
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
if (*(int *)mp->b_cont->b_rptr == ms->ms_readformat) {
break;
}
ms->ms_readformat = *(int *)mp->b_cont->b_rptr;
/*
* Flush mouse buffer because the messages upstream of us
* are in the old format.
*/
usbms_flush(usbmsp);
break;
case VUIDGFORMAT:
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = ms->ms_readformat;
datap->b_wptr += sizeof (int);
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case VUIDGADDR:
case VUIDSADDR:
err = miocpullup(mp, sizeof (Vuid_addr_probe));
if (err != 0)
break;
addr_probe = (Vuid_addr_probe *)mp->b_cont->b_rptr;
if (addr_probe->base != VKEY_FIRST) {
err = ENODEV;
break;
}
if (iocp->ioc_cmd == VUIDSADDR)
ms->ms_vuidaddr = addr_probe->data.next;
else
addr_probe->data.current = ms->ms_vuidaddr;
break;
case MSIOGETPARMS:
if ((datap = allocb(sizeof (Ms_parms), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (Ms_parms);
goto allocfailure;
}
err = usbms_getparms((Ms_parms *)datap->b_wptr, usbmsp);
datap->b_wptr += sizeof (Ms_parms);
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (Ms_parms);
break;
case MSIOSETPARMS:
err = miocpullup(mp, sizeof (Ms_parms));
if (err != 0)
break;
err = usbms_setparms((Ms_parms *)mp->b_cont->b_rptr, usbmsp);
break;
case MSIOBUTTONS:
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = (int)usbmsp->usbms_num_buttons;
datap->b_wptr += sizeof (int);
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case VUIDGWHEELCOUNT:
/*
* New IOCTL support. Since it's explicitly mentioned that
* you can't add more ioctls to stream head's hard coded
* list, we have to do the transparent ioctl processing
* which is heavy.
*/
/* Currently support for only one wheel */
if (iocp->ioc_count == TRANSPARENT) {
transparent = 1;
if (err = usbms_make_copyreq(mp, 0, 0, sizeof (int),
0, M_COPYOUT)) {
break;
}
}
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*((int *)datap->b_wptr) = (usbmsp->usbms_num_wheels ? 1 : 0);
datap->b_wptr += sizeof (int);
if (mp->b_cont) {
freemsg(mp->b_cont);
mp->b_cont = NULL;
}
mp->b_cont = datap;
if (transparent) {
qreply(q, mp);
return;
}
break;
case VUIDGWHEELINFO:
if (iocp->ioc_count == TRANSPARENT) {
if (err = usbms_make_copyreq(mp,
sizeof (usbms_iocstate_t),
USBMS_GETSTRUCT,
sizeof (wheel_info),
0,
M_COPYIN)) {
break;
}
/*
* If there is no b_cont the earlier func. will fail.
* Hence there is no need for an explicit check here.
*/
freemsg(mp->b_cont);
mp->b_cont = (mblk_t *)NULL;
qreply(q, mp);
return;
}
if (mp->b_cont == NULL || iocp->ioc_count !=
sizeof (wheel_info)) {
err = EINVAL;
break;
}
datap = mp->b_cont;
err = usbms_service_wheel_info(q, datap);
break;
case VUIDGWHEELSTATE:
if (iocp->ioc_count == TRANSPARENT) {
if (err = usbms_make_copyreq(mp,
sizeof (usbms_iocstate_t),
USBMS_GETSTRUCT,
sizeof (wheel_state),
0,
M_COPYIN)) {
break;
}
freemsg(mp->b_cont);
mp->b_cont = (mblk_t *)NULL;
qreply(q, mp);
return;
}
if ((mp->b_cont == NULL) ||
(iocp->ioc_count != sizeof (wheel_state))) {
err = EINVAL;
break;
}
datap = mp->b_cont;
err = usbms_service_wheel_state(q, datap, VUIDGWHEELSTATE);
break;
case VUIDSWHEELSTATE:
if (iocp->ioc_count == TRANSPARENT) {
if (err = usbms_make_copyreq(mp,
sizeof (usbms_iocstate_t),
USBMS_GETSTRUCT,
sizeof (wheel_state),
0,
M_COPYIN)) {
break;
}
freemsg(mp->b_cont);
mp->b_cont = (mblk_t *)NULL;
qreply(q, mp);
return;
}
if (mp->b_cont == NULL) {
err = EINVAL;
break;
}
datap = mp->b_cont;
err = usbms_service_wheel_state(q, datap, VUIDSWHEELSTATE);
break;
case MSIOSRESOLUTION:
if (iocp->ioc_count == TRANSPARENT) {
if (err = usbms_make_copyreq(mp,
sizeof (usbms_iocstate_t),
USBMS_GETSTRUCT,
sizeof (Ms_screen_resolution),
0,
M_COPYIN)) {
break;
}
freemsg(mp->b_cont);
mp->b_cont = (mblk_t *)NULL;
qreply(q, mp);
return;
}
if (mp->b_cont == NULL) {
err = EINVAL;
break;
}
datap = mp->b_cont;
err = usbms_get_screen_parms(q, datap);
break;
default:
putnext(q, mp); /* pass it down the line */
return;
} /* switch */
if (err != 0)
miocnak(q, mp, 0, err);
else {
iocp->ioc_rval = 0;
iocp->ioc_error = 0;
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
}
return;
allocfailure:
/*
* We needed to allocate something to handle this "ioctl", but
* couldn't; save this "ioctl" and arrange to get called back when
* it's more likely that we can get what we need.
* If there's already one being saved, throw it out, since it
* must have timed out.
*/
freemsg(usbmsp->usbms_iocpending);
usbmsp->usbms_iocpending = mp;
if (usbmsp->usbms_reioctl_id) {
qunbufcall(q, (bufcall_id_t)usbmsp->usbms_reioctl_id);
}
usbmsp->usbms_reioctl_id = qbufcall(q, ioctlrespsize, BPRI_HI,
(void (*)())usbms_reioctl,
(void *)usbmsp);
}
/*
* M_IOCDATA processing for IOCTL's: VUIDGWHEELCOUNT, VUIDGWHEELINFO,
* VUIDGWHEELSTATE, VUIDSWHEELSTATE & MSIOSRESOLUTION.
*/
static void
usbms_miocdata(register queue_t *q,
register mblk_t *mp)
{
struct copyresp *copyresp;
struct iocblk *iocbp;
mblk_t *datap;
mblk_t *ioctmp;
usbms_iocstate_t *usbmsioc;
int err = 0;
copyresp = (struct copyresp *)mp->b_rptr;
iocbp = (struct iocblk *)mp->b_rptr;
if (copyresp->cp_rval) {
err = EAGAIN;
goto err;
}
switch (copyresp->cp_cmd) {
case VUIDGWHEELCOUNT:
usbms_ack_ioctl(mp);
break;
case VUIDGWHEELINFO:
ioctmp = copyresp->cp_private;
usbmsioc = (usbms_iocstate_t *)ioctmp->b_rptr;
if (usbmsioc->ioc_state == USBMS_GETSTRUCT) {
if (mp->b_cont == NULL) {
err = EINVAL;
break;
}
datap = (mblk_t *)mp->b_cont;
if (err = usbms_service_wheel_info(q, datap)) {
goto err;
}
if (err = usbms_make_copyreq(mp, 0, USBMS_GETRESULT,
sizeof (wheel_info), 0, M_COPYOUT)) {
goto err;
}
} else if (usbmsioc->ioc_state == USBMS_GETRESULT) {
freemsg(ioctmp);
usbms_ack_ioctl(mp);
}
break;
case VUIDGWHEELSTATE:
ioctmp = (mblk_t *)copyresp->cp_private;
usbmsioc = (usbms_iocstate_t *)ioctmp->b_rptr;
if (usbmsioc->ioc_state == USBMS_GETSTRUCT) {
if (mp->b_cont == NULL) {
err = EINVAL;
break;
}
if (err = usbms_service_wheel_state(q, mp->b_cont,
VUIDGWHEELSTATE)) {
goto err;
}
if (err = usbms_make_copyreq(mp, 0, USBMS_GETRESULT,
sizeof (wheel_state), 0, M_COPYOUT)) {
goto err;
}
} else if (usbmsioc->ioc_state == USBMS_GETRESULT) {
freemsg(ioctmp);
usbms_ack_ioctl(mp);
}
break;
case VUIDSWHEELSTATE:
ioctmp = (mblk_t *)copyresp->cp_private;
usbmsioc = (usbms_iocstate_t *)ioctmp->b_rptr;
if (mp->b_cont == NULL) {
err = EINVAL;
break;
}
if (err = usbms_service_wheel_state(q, mp->b_cont,
VUIDSWHEELSTATE)) {
goto err;
}
freemsg(ioctmp);
usbms_ack_ioctl(mp);
break;
case MSIOSRESOLUTION:
ioctmp = (mblk_t *)copyresp->cp_private;
usbmsioc = (usbms_iocstate_t *)ioctmp->b_rptr;
if (mp->b_cont == NULL) {
err = EINVAL;
break;
}
if (err = usbms_get_screen_parms(q, mp->b_cont)) {
goto err;
}
freemsg(ioctmp);
usbms_ack_ioctl(mp);
break;
default:
err = EINVAL;
break;
}
err:
if (err) {
mp->b_datap->db_type = M_IOCNAK;
if (mp->b_cont) {
freemsg(mp->b_cont);
mp->b_cont = (mblk_t *)NULL;
}
if (copyresp->cp_private) {
freemsg((mblk_t *)copyresp->cp_private);
copyresp->cp_private = (mblk_t *)NULL;
}
iocbp->ioc_count = 0;
iocbp->ioc_error = err;
}
qreply(q, mp);
}
/*
* usbms_reioctl() :
* This function is set up as call-back function should an ioctl fail.
* It retries the ioctl.
*/
static void
usbms_reioctl(void * usbms_addr)
{
usbms_state_t *usbmsp = (usbms_state_t *)usbms_addr;
register queue_t *q;
register mblk_t *mp;
q = usbmsp->usbms_wq_ptr;
if ((mp = usbmsp->usbms_iocpending) != NULL) {
usbmsp->usbms_iocpending = NULL; /* not pending any more */
usbms_ioctl(q, mp);
}
}
/*
* usbms_getparms() :
* Called from MSIOGETPARMS ioctl to get the
* current jitter_thesh, speed_law and speed_limit
* values.
*/
static int
usbms_getparms(register Ms_parms *data,
usbms_state_t *usbmsp)
{
data->jitter_thresh = usbmsp->usbms_jitter_thresh;
data->speed_law = usbmsp->usbms_speedlaw;
data->speed_limit = usbmsp->usbms_speedlimit;
return (0);
}
/*
* usbms_setparms() :
* Called from MSIOSETPARMS ioctl to set the
* current jitter_thesh, speed_law and speed_limit
* values.
*/
static int
usbms_setparms(register Ms_parms *data,
usbms_state_t *usbmsp)
{
usbmsp->usbms_jitter_thresh = data->jitter_thresh;
usbmsp->usbms_speedlaw = data->speed_law;
usbmsp->usbms_speedlimit = data->speed_limit;
return (0);
}
/*
* usbms_flush() :
* Resets the ms_softc structure to default values
* and sends M_FLUSH above.
*/
static void
usbms_flush(usbms_state_t *usbmsp)
{
register struct ms_softc *ms = &usbmsp->usbms_softc;
register queue_t *q;
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"usbms_flush entering");
ms->ms_oldoff = 0;
ms->ms_eventstate = EVENT_BUT(usbmsp->usbms_num_buttons);
usbmsp->usbms_buf->mb_off = 0;
ms->ms_prevbuttons = (char)USB_NO_BUT_PRESSED;
usbmsp->usbms_oldbutt = ms->ms_prevbuttons;
if ((q = usbmsp->usbms_rq_ptr) != NULL && q->q_next != NULL) {
(void) putnextctl1(q, M_FLUSH, FLUSHR);
}
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"usbms_flush exiting");
}
/*
* usbms_rput() :
* Put procedure for input from driver end of stream (read queue).
*/
static void
usbms_rput(queue_t *q,
mblk_t *mp)
{
usbms_state_t *usbmsp = q->q_ptr;
mblk_t *tmp_mp;
ushort_t limit = (usbmsp->usbms_idf).tlen;
/* Maintain the original mp */
tmp_mp = mp;
if (usbmsp == 0) {
freemsg(mp); /* nobody's listening */
return;
}
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;
case M_BREAK:
/*
* We don't have to handle this
* because nothing is sent from the downstream
*/
freemsg(mp);
return;
case M_DATA:
if (!(usbmsp->usbms_flags & USBMS_OPEN)) {
freemsg(mp); /* not ready to listen */
return;
}
break;
case M_CTL:
usbms_mctl_receive(q, mp);
return;
case M_ERROR:
usbmsp->usbms_protoerr = 1;
usbmsp->usbms_flags &= ~USBMS_QWAIT;
freemsg(mp);
return;
default:
putnext(q, mp);
return;
}
/*
* A data message, consisting of bytes from the mouse.
* Make sure there are atleast "limit" number of bytes.
*/
if (((tmp_mp->b_wptr - tmp_mp->b_rptr) < limit) ||
(((tmp_mp->b_wptr - tmp_mp->b_rptr) == limit) &&
(usbmsp->usbms_rptid != HID_REPORT_ID_UNDEFINED))) {
freemsg(mp);
return;
}
do {
if (usbmsp->usbms_rptid != HID_REPORT_ID_UNDEFINED) {
if (*(tmp_mp->b_rptr) != usbmsp->usbms_rptid) {
freemsg(mp);
return;
} else {
/* We skip the report id prefix. */
tmp_mp->b_rptr++;
}
}
usbms_input(usbmsp, tmp_mp);
} while ((tmp_mp = tmp_mp->b_cont) != NULL); /* next block, if any */
freemsg(mp);
}
/*
* usbms_mctl_receive() :
* Handle M_CTL messages from hid. If
* we don't understand the command, free message.
*/
static void
usbms_mctl_receive(register queue_t *q,
register mblk_t *mp)
{
usbms_state_t *usbmsd = (usbms_state_t *)q->q_ptr;
struct iocblk *iocp;
caddr_t data;
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_GET_PARSER_HANDLE:
if ((data != NULL) &&
(iocp->ioc_count == sizeof (hidparser_handle_t)) &&
((mp->b_cont->b_wptr - mp->b_cont->b_rptr) ==
iocp->ioc_count)) {
usbmsd->usbms_report_descr_handle =
*(hidparser_handle_t *)data;
} else {
usbmsd->usbms_report_descr_handle = NULL;
}
freemsg(mp);
usbmsd->usbms_flags &= ~USBMS_QWAIT;
break;
case HID_SET_PROTOCOL:
usbmsd->usbms_flags &= ~USBMS_QWAIT;
/* FALLTHRU */
default:
freemsg(mp);
break;
}
}
/*
* usbms_input() :
*
* Mouse input routine; process a byte received from a mouse and
* assemble into a mouseinfo message for the window system.
*
* The USB mouse send a three-byte packet organized as
* button, dx, dy
* where dx and dy can be any signed byte value. The mouseinfo message
* is organized as
* dx, dy, button, timestamp
* Our strategy is to collect but, dx & dy three-byte packet, then
* send the mouseinfo message up.
*
* Basic algorithm: throw away bytes until we get a [potential]
* button byte. Collect button; Collect dx; Collect dy; Send button,
* dx, dy, timestamp.
*
* Watch out for overflow!
*
* cxyz, sxyz and ixyz stand for different length value in sample data.
*/
static void
usbms_input(usbms_state_t *usbmsp,
mblk_t *mp)
{
register struct usbmousebuf *b;
register struct usbmouseinfo *mi;
register int jitter_radius;
register int temp = 0;
register int32_t nbutt;
ushort_t i;
char c;
char cxyz = 0; /* for one byte */
short sxyz = 0; /* for two bytes */
int ixyz = 0; /* for four bytes */
nbutt = usbmsp->usbms_num_buttons;
b = usbmsp->usbms_buf;
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR, usbms_log_handle,
"usbms_input entering");
if (b == NULL) {
return;
}
mi = &b->mb_info[b->mb_off];
/*
* Lower 3 bits are middle, right, left.
*/
c = mp->b_rptr[(usbmsp->usbms_idf).bpos];
mi->mi_buttons = (char)USB_NO_BUT_PRESSED;
if (c & USBMS_BUT(1)) { /* left button is pressed */
mi->mi_buttons = mi->mi_buttons & USB_LEFT_BUT_PRESSED;
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR,
usbms_log_handle,
"left button pressed");
}
if (c & USBMS_BUT(2)) { /* right button is pressed */
mi->mi_buttons = mi->mi_buttons & USB_RIGHT_BUT_PRESSED;
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR,
usbms_log_handle,
"right button pressed");
}
if (c & USBMS_BUT(3)) { /* middle button is pressed */
mi->mi_buttons = mi->mi_buttons &
USB_MIDDLE_BUT_PRESSED;
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR,
usbms_log_handle,
"middle button pressed");
}
if (nbutt > 3) {
for (i = 4; i < (nbutt + 1); i++) {
if (c & USBMS_BUT(i)) {
mi->mi_buttons = mi->mi_buttons &
USB_BUT_PRESSED(i);
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR,
usbms_log_handle,
"%d button pressed", i);
}
}
}
/*
* According to the length of the X coordinate,
* the current delta X is got from the sample.
*/
switch ((usbmsp->usbms_idf).xlen) {
case 1:
cxyz = mp->b_rptr[((usbmsp->usbms_idf).xpos)];
temp = (int)(mi->mi_x + cxyz);
break;
case 2:
for (i = 0; i < 2; i++) {
sxyz = sxyz | (mp->b_rptr[((usbmsp->usbms_idf).xpos)
+ i] << (8 * i));
}
temp = (int)(mi->mi_x + sxyz);
break;
case 4:
for (i = 0; i < 4; i++) {
ixyz = ixyz | (mp->b_rptr[((usbmsp->usbms_idf).xpos)
+ i] << (8 * i));
}
temp = (int)(mi->mi_x + ixyz);
break;
}
if (((usbmsp->usbms_idf).xlen) == 1) {
mi->mi_x = USB_BYTECLIP(temp);
} else {
mi->mi_x = temp;
}
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR,
usbms_log_handle, "x = %d", (int)mi->mi_x);
uniqtime32(&mi->mi_time); /* record time when sample arrived */
/*
* According to the length of the Y coordinate,
* the current delta Y is got from the sample.
*/
cxyz = sxyz = ixyz = 0;
switch ((usbmsp->usbms_idf).ylen) {
case 1:
cxyz = mp->b_rptr[((usbmsp->usbms_idf).ypos)];
temp = (int)(mi->mi_y + cxyz);
break;
case 2:
for (i = 0; i < 2; i++) {
sxyz = sxyz | (mp->b_rptr[((usbmsp->usbms_idf).ypos)
+ i] << (8 * i));
}
temp = (int)(mi->mi_y + sxyz);
break;
case 4:
for (i = 0; i < 4; i++) {
ixyz = ixyz | (mp->b_rptr[((usbmsp->usbms_idf).ypos)
+ i] << (8 * i));
}
temp = (int)(mi->mi_y + ixyz);
break;
}
if (((usbmsp->usbms_idf).ylen) == 1) {
mi->mi_y = USB_BYTECLIP(temp);
} else {
mi->mi_y = temp;
}
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR, usbms_log_handle,
"y = %d", (int)mi->mi_y);
/*
* Check the wheel data in the current event.
* If it exists, the wheel data is got from the sample.
*/
if (usbmsp->usbms_num_wheels) {
cxyz = sxyz = ixyz = temp = 0;
switch ((usbmsp->usbms_idf).zlen) {
case 1:
cxyz = mp->b_rptr[((usbmsp->usbms_idf).zpos)];
temp = (int)(mi->mi_z + cxyz);
break;
case 2:
for (i = 0; i < 2; i++) {
sxyz = sxyz |
(mp->b_rptr[((usbmsp->usbms_idf).zpos)
+ i] << (8 * i));
}
temp = (int)(mi->mi_z + sxyz);
break;
case 4:
for (i = 0; i < 4; i++) {
ixyz = ixyz |
(mp->b_rptr[((usbmsp->usbms_idf).zpos)
+ i] << (8 * i));
}
temp = (int)(mi->mi_z + ixyz);
break;
}
if (((usbmsp->usbms_idf).zlen) == 1) {
mi->mi_z = USB_BYTECLIP(temp);
} else {
mi->mi_z = temp;
}
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR, usbms_log_handle,
"z = %d", (int)mi->mi_z);
}
if (usbmsp->usbms_jitter) {
(void) quntimeout(usbmsp->usbms_rq_ptr,
(timeout_id_t)usbmsp->usbms_timeout_id);
usbmsp->usbms_jitter = 0;
}
if (!usbmsp->usbms_num_wheels) {
mi->mi_z = 0;
}
/*
* If there is a wheel movement or a change in the button state,
* send the data up immediately.
*/
if (!(mi->mi_z) && (mi->mi_buttons == usbmsp->usbms_oldbutt)) {
/*
* Buttons did not change; did position?
*/
if (mi->mi_x == 0 && mi->mi_y == 0) {
/* no, position did not change */
return;
}
/*
* Did the mouse move more than the jitter threshhold?
*/
jitter_radius = usbmsp->usbms_jitter_thresh;
if (USB_ABS((int)mi->mi_x) <= jitter_radius &&
USB_ABS((int)mi->mi_y) <= jitter_radius) {
/*
* Mouse moved less than the jitter threshhold.
* Don't indicate an event; keep accumulating motions.
* After "jittertimeout" ticks expire, treat
* the accumulated delta as the real delta.
*/
usbmsp->usbms_jitter = 1;
usbmsp->usbms_timeout_id =
qtimeout(usbmsp->usbms_rq_ptr, (void (*)())usbms_incr,
(void *)usbmsp, (clock_t)usbmsp->usbms_jittertimeout);
return;
}
}
usbmsp->usbms_oldbutt = mi->mi_buttons;
usbms_incr(usbmsp);
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR, usbms_log_handle,
"usbms_input exiting");
}
/*
* usbms_incr() :
* Increment the mouse sample pointer.
* Called either immediately after a sample or after a jitter timeout.
*/
static void
usbms_incr(void *arg)
{
usbms_state_t *usbmsp = arg;
register struct ms_softc *ms = &usbmsp->usbms_softc;
register struct usbmousebuf *b;
register struct usbmouseinfo *mi;
register int xc, yc, zc;
register int wake;
register int speedl = usbmsp->usbms_speedlimit;
register int xabs, yabs;
/*
* No longer waiting for jitter timeout
*/
usbmsp->usbms_jitter = 0;
b = usbmsp->usbms_buf;
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR, usbms_log_handle,
"usbms_incr entering");
if (b == NULL) {
return;
}
mi = &b->mb_info[b->mb_off];
if (usbmsp->usbms_speedlaw) {
xabs = USB_ABS((int)mi->mi_x);
yabs = USB_ABS((int)mi->mi_y);
if (xabs > speedl || yabs > speedl) {
usbmsp->usbms_speed_count++;
}
if (xabs > speedl) {
mi->mi_x = 0;
}
if (yabs > speedl) {
mi->mi_y = 0;
}
}
xc = yc = zc = 0;
/* See if we need to wake up anyone waiting for input */
wake = b->mb_off == ms->ms_oldoff;
/* Adjust circular buffer pointer */
if (++b->mb_off >= b->mb_size) {
b->mb_off = 0;
mi = b->mb_info;
} else {
mi++;
}
/*
* If over-took read index then flush buffer so that mouse state
* is consistent.
*/
if (b->mb_off == ms->ms_oldoff) {
if (overrun_msg) {
USB_DPRINTF_L1(PRINT_MASK_ALL, usbms_log_handle,
"Mouse buffer flushed when overrun.");
}
usbms_flush(usbmsp);
overrun_cnt++;
mi = b->mb_info;
}
/* Remember current buttons and fractional part of x & y */
mi->mi_buttons = (char)USB_NO_BUT_PRESSED;
mi->mi_x = xc;
mi->mi_y = yc;
mi->mi_z = zc;
if (wake) {
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR, usbms_log_handle,
"usbms_incr run service");
qenable(usbmsp->usbms_rq_ptr); /* run the service proc */
}
USB_DPRINTF_L3(PRINT_MASK_INPUT_INCR, usbms_log_handle,
"usbms_incr exiting");
}
/*
* usbms_check_for_wheels
* return SUCCESS if wheel is found, else return FAILURE
*/
static int
usbms_check_for_wheels(usbms_state_t *usbmsp)
{
int rval, report_id;
if (usbmsp->usbms_report_descr_handle) {
/* Get the report id that has mouse data */
if (hidparser_get_usage_attribute(
usbmsp->usbms_report_descr_handle,
0, /* Doesn't matter */
HIDPARSER_ITEM_INPUT,
HID_GENERIC_DESKTOP,
HID_GD_X,
HIDPARSER_ITEM_REPORT_ID,
&usbmsp->usbms_rptid) == HIDPARSER_NOT_FOUND) {
usbmsp->usbms_rptid = HID_REPORT_ID_UNDEFINED;
report_id = 0;
} else {
report_id = usbmsp->usbms_rptid;
}
/* find no. of wheels in this report */
rval = hidparser_get_usage_attribute(
usbmsp->usbms_report_descr_handle,
report_id,
HIDPARSER_ITEM_INPUT,
HID_GENERIC_DESKTOP,
HID_GD_WHEEL,
HIDPARSER_ITEM_REPORT_COUNT,
&usbmsp->usbms_num_wheels);
if (rval == HIDPARSER_SUCCESS) {
/*
* Found wheel. By default enable the wheel.
* Currently only enable only the first wheel.
*/
usbmsp->usbms_wheel_state_bf |=
VUID_WHEEL_STATE_ENABLED;
return (USB_SUCCESS);
}
}
usbmsp->usbms_num_wheels = 0;
return (USB_FAILURE);
}
/*
* usbms_make_copyreq
* helper function for usbms ioctls
*/
static int
usbms_make_copyreq(mblk_t *mp,
uint_t pvtsize,
uint_t state,
uint_t reqsize,
uint_t contsize,
uint_t copytype)
{
struct copyreq *cq;
struct copyresp *cr;
mblk_t *ioctmp;
mblk_t *conttmp;
usbms_iocstate_t *usbmsioc;
if ((!pvtsize) && state) {
cr = (struct copyresp *)mp->b_rptr;
ioctmp = cr->cp_private;
}
cq = (struct copyreq *)mp->b_rptr;
if (mp->b_cont == NULL) {
return (EINVAL);
}
cq->cq_addr = *((caddr_t *)mp->b_cont->b_rptr);
cq->cq_size = reqsize;
cq->cq_flag = 0;
if (pvtsize) {
ioctmp = (mblk_t *)allocb(pvtsize, BPRI_MED);
if (ioctmp == NULL) {
return (EAGAIN);
}
cq->cq_private = ioctmp;
ioctmp = cq->cq_private;
}
if (state) {
usbmsioc = (usbms_iocstate_t *)ioctmp->b_rptr;
usbmsioc->ioc_state = state;
if (pvtsize) {
usbmsioc->u_addr = cq->cq_addr;
} else {
cq->cq_addr = usbmsioc->u_addr;
cq->cq_private = ioctmp;
}
ioctmp->b_wptr = ioctmp->b_rptr + sizeof (usbms_iocstate_t);
}
if (contsize) {
conttmp = (mblk_t *)allocb(contsize, BPRI_MED);
if (conttmp == NULL) {
return (EAGAIN);
}
if (mp->b_cont) {
freemsg(mp->b_cont);
mp->b_cont = conttmp;
}
}
mp->b_datap->db_type = copytype;
mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
return (USB_SUCCESS);
}
static int
usbms_service_wheel_info(register queue_t *q, register mblk_t *datap)
{
wheel_info *wi;
usbms_state_t *usbmsp = (usbms_state_t *)q->q_ptr;
uint_t err;
wi = (wheel_info *)datap->b_rptr;
if (wi->vers != VUID_WHEEL_INFO_VERS) {
err = EINVAL;
return (err);
}
if (wi->id > (usbmsp->usbms_num_wheels - 1)) {
err = EINVAL;
return (err);
}
wi->format = (usbmsp->usbms_wheel_orient_bf & (1 << wi->id)) ?
VUID_WHEEL_FORMAT_HORIZONTAL : VUID_WHEEL_FORMAT_VERTICAL;
return (USB_SUCCESS);
}
static int
usbms_service_wheel_state(register queue_t *q,
register mblk_t *datap,
register uint_t cmd)
{
wheel_state *ws;
uint_t err;
usbms_state_t *usbmsp = (usbms_state_t *)q->q_ptr;
ws = (wheel_state *)datap->b_rptr;
if (ws->vers != VUID_WHEEL_STATE_VERS) {
err = EINVAL;
return (err);
}
if (ws->id > (usbmsp->usbms_num_wheels - 1)) {
err = EINVAL;
return (err);
}
switch (cmd) {
case VUIDGWHEELSTATE:
ws->stateflags = (usbmsp->usbms_wheel_state_bf >> ws->id) &
VUID_WHEEL_STATE_ENABLED;
break;
case VUIDSWHEELSTATE:
usbmsp->usbms_wheel_state_bf = (ws->stateflags << ws->id) |
(~(1 << ws->id) & usbmsp->usbms_wheel_state_bf);
break;
default:
err = EINVAL;
return (err);
}
return (USB_SUCCESS);
}
/*
* usbms_get_screen_parms() :
* Called from MSIOSRESOLUTION ioctl to get the
* current screen height/width params from X.
*/
static int
usbms_get_screen_parms(register queue_t *q,
register mblk_t *datap)
{
usbms_state_t *usbmsp = (usbms_state_t *)q->q_ptr;
Ms_screen_resolution *res = &(usbmsp->usbms_resolution);
Ms_screen_resolution *data;
data = (Ms_screen_resolution *)datap->b_rptr;
res->height = data->height;
res->width = data->width;
return (USB_SUCCESS);
}
static void
usbms_ack_ioctl(mblk_t *mp)
{
struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
mp->b_datap->db_type = M_IOCACK;
mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
iocbp->ioc_error = 0;
iocbp->ioc_count = 0;
iocbp->ioc_rval = 0;
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
mp->b_cont = NULL;
}
}
/*
* usbms_read_input_data_format() :
* Get the mouse packet length and usages' length.
* Check whether X and Y are relative or absolute.
*
* If they are absolute, the X and Y logical max values
* will be got. A firm event will be created and sent
* to the upper level.
*/
int
usbms_read_input_data_format(usbms_state_t *usbmsp)
{
hidparser_rpt_t *ms_rpt;
uint_t i, j, button_page;
uint_t limit = 0;
uint32_t rptcnt, rptsz;
usbms_idf *idf = &(usbmsp->usbms_idf);
Ms_screen_resolution *res = &(usbmsp->usbms_resolution);
Firm_event *fep;
mblk_t *mb;
register queue_t *q;
int rval;
/* allocate hidparser report structure */
ms_rpt = kmem_zalloc(sizeof (hidparser_rpt_t), KM_SLEEP);
/*
* Check what is the total length of the mouse packet
* and get the usages and their lengths in order
*/
rval = hidparser_get_usage_list_in_order(
usbmsp->usbms_report_descr_handle,
usbmsp->usbms_rptid,
HIDPARSER_ITEM_INPUT,
ms_rpt);
if (rval != HIDPARSER_SUCCESS) {
kmem_free(ms_rpt, sizeof (hidparser_rpt_t));
return (USB_FAILURE);
}
for (i = 0; i < ms_rpt->no_of_usages; i++) {
rptcnt = ms_rpt->usage_descr[i].rptcnt;
rptsz = ms_rpt->usage_descr[i].rptsz;
if (rptcnt == 0) {
continue;
}
button_page = 0;
for (j = 0; j < rptcnt; j++) {
if (ms_rpt->usage_descr[j + i].usage_id == HID_GD_X) {
idf->xpos = limit + (j * rptsz);
idf->xpos /= 8;
idf->xlen = rptsz;
if (idf->xlen % 8) {
idf->xlen = (idf->xlen / 8) + 1;
} else {
idf->xlen /= 8;
}
} else if (ms_rpt->usage_descr[j + i].usage_id ==
HID_GD_Y) {
idf->ypos = limit + (j * rptsz);
idf->ypos /= 8;
idf->ylen = rptsz;
if (idf->ylen % 8) {
idf->ylen = (idf->ylen / 8) + 1;
} else {
idf->ylen /= 8;
}
} else if (ms_rpt->usage_descr[j + i].usage_id ==
HID_GD_WHEEL) {
idf->zpos = limit + (j * rptsz);
idf->zpos /= 8;
idf->zlen = rptsz;
if (idf->zlen % 8) {
idf->zlen = (idf->zlen / 8) + 1;
} else {
idf->zlen /= 8;
}
} else if (ms_rpt->usage_descr[j + i].usage_page ==
HID_BUTTON_PAGE) {
if (ms_rpt->usage_descr[j + i].usage_id !=
HID_USAGE_UNDEFINED) {
idf->bpos = limit;
}
button_page = 1;
break;
}
}
i += j;
/* enter the next valid usage */
if (button_page == 0) {
i --;
}
limit = limit + (rptcnt * rptsz);
}
kmem_free(ms_rpt, sizeof (hidparser_rpt_t));
/* get the length of sending data */
idf->tlen = limit / 8;
/* Check whether X and Y are relative or absolute */
rval = hidparser_get_main_item_data_descr(
usbmsp->usbms_report_descr_handle,
usbmsp->usbms_rptid,
HIDPARSER_ITEM_INPUT,
HID_GENERIC_DESKTOP,
HID_GD_X,
&idf->xattr);
if (rval != HIDPARSER_SUCCESS) {
return (USB_FAILURE);
}
/* For the time being assume that Y also has the same attr */
idf->yattr = idf->xattr;
/* get the logical_maximum for X and Y respectively */
if (!(idf->xattr & HID_MAIN_ITEM_RELATIVE)) {
/* the data format can't be parsed correctly */
if (limit % 8) {
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"Wrong data packet include %d bits", limit);
return (USB_FAILURE);
}
if (hidparser_get_usage_attribute(
usbmsp->usbms_report_descr_handle,
usbmsp->usbms_rptid,
HIDPARSER_ITEM_INPUT,
HID_GENERIC_DESKTOP,
HID_GD_X,
HIDPARSER_ITEM_LOGICAL_MAXIMUM,
&usbmsp->usbms_logical_Xmax) != HIDPARSER_SUCCESS) {
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"fail to get X logical max.");
return (USB_FAILURE);
}
if (hidparser_get_usage_attribute(
usbmsp->usbms_report_descr_handle,
usbmsp->usbms_rptid,
HIDPARSER_ITEM_INPUT,
HID_GENERIC_DESKTOP,
HID_GD_Y,
HIDPARSER_ITEM_LOGICAL_MAXIMUM,
&usbmsp->usbms_logical_Ymax) != HIDPARSER_SUCCESS) {
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"fail to get Y logical max.");
return (USB_FAILURE);
}
if (usbmsp->usbms_logical_Xmax == 0) {
USB_DPRINTF_L3(PRINT_MASK_ALL,
usbms_log_handle,
"X logical max value is zero");
return (USB_FAILURE);
}
if (usbmsp->usbms_logical_Ymax == 0) {
USB_DPRINTF_L3(PRINT_MASK_ALL,
usbms_log_handle,
"Y logical max value is zero");
return (USB_FAILURE);
}
res->height = USBMS_DEFAULT_RES_HEIGHT;
res->width = USBMS_DEFAULT_RES_WIDTH;
/* The wheel is not supported in current remote kvms. */
usbmsp->usbms_num_wheels = 0;
if ((mb = allocb(sizeof (Firm_event), BPRI_HI)) ==
NULL) {
return (USB_NO_RESOURCES);
} else {
/*
* notify the upper that it is an absolute mouse
*/
q = usbmsp->usbms_rq_ptr;
fep = (Firm_event *)mb->b_wptr;
fep->id = MOUSE_TYPE_ABSOLUTE;
fep->pair_type = FE_PAIR_NONE;
fep->pair = NULL;
fep->value = NULL;
mb->b_wptr += sizeof (Firm_event);
putnext(q, mb);
}
} else {
/* general data format of relative mouse */
idf->xlen = idf->ylen = idf->zlen = 1;
idf->xpos = 1;
idf->ypos = 2;
idf->zpos = 3;
/* three-byte packet for general mouse */
if (limit % 8) {
USB_DPRINTF_L3(PRINT_MASK_ALL, usbms_log_handle,
"Data per packet include %d bits", limit);
idf->tlen = 3;
}
}
return (USB_SUCCESS);
}