usbms.c revision 4610e4a00999c6d2291b3fc263926b890ec500a5
/*
* 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
* 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/vuid_event.h>
#include <sys/vuid_wheel.h>
/* debugging information */
static usb_log_handle_t usbms_log_handle;
static struct streamtab usbms_streamtab;
"usbms",
};
/*
* Module linkage information for the kernel.
*/
static struct modlstrmod modlstrmod = {
"USB mouse streams %I%",
&fsw
};
static struct modlinkage modlinkage = {
(void *)&modlstrmod,
};
int
_init(void)
{
if (rval == 0) {
}
return (rval);
}
int
_fini(void)
{
if (rval == 0) {
}
return (rval);
}
int
{
}
/* Function prototypes */
static void usbms_reioctl(void *);
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,
static void usbms_rserv(queue_t *q);
static void usbms_miocdata(
register queue_t *q,
static void usbms_resched(void *);
static int usbms_getparms(
static int usbms_setparms(
static int usbms_get_screen_parms(
register queue_t *q,
static void usbms_incr(void *);
static void usbms_input(
static void usbms_rserv_vuid_button(
queue_t *q,
struct usbmouseinfo *mi,
static void usbms_rserv_vuid_event_y(
queue_t *q,
struct usbmouseinfo *mi,
static void usbms_rserv_vuid_event_x(
queue_t *q,
struct usbmouseinfo *mi,
static void usbms_rserv_vuid_event_wheel(
queue_t *,
struct usbmouseinfo *,
mblk_t **,
static int usbms_check_for_wheels(usbms_state_t *);
static int usbms_make_copyreq(
mblk_t *,
static int usbms_service_wheel_info(
queue_t *,
mblk_t *);
static int usbms_service_wheel_state(
queue_t *,
mblk_t *,
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 */
(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 */
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.
*/
/*
* Regular STREAMS Entry points
*/
/*
* usbms_open() :
* open() entry point for the USB mouse module.
*/
/*ARGSUSED*/
static int
usbms_open(queue_t *q,
int flag,
int sflag,
{
register struct usbmousebuf *mousebufp;
/* Clone opens are not allowed */
return (EINVAL);
/* If the module is already open, just return */
if (q->q_ptr) {
return (0);
}
/* allocate usbms state structure */
usbmsp->usbms_rq_ptr = q;
qprocson(q);
/*
* Set up private data.
*/
/*
* Initially set the format to MS_VUID_FORMAT
*/
/*
* Allocate buffer and initialize data.
*/
KM_SLEEP);
/* Truncation will happen */
sizeof (struct usbmousebuf)) /
sizeof (struct usbmouseinfo));
sizeof (struct usbmousebuf));
/* request hid report descriptor from HID */
qprocsoff(q);
return (ENOMEM);
}
/*
* 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).
*/
if (qwait_sig(q) == 0) {
qprocsoff(q);
return (EINTR);
}
}
0,
0,
usbms_log_handle, "Num of buttons is : %d",
} else {
"hidparser_get_usage_attribute failed : "
"Set to default number of buttons(3).");
}
} else {
usbms_log_handle, "usbms: Invalid HID "
"Descriptor Tree. Set to default value(3 buttons).");
}
/* check if this mouse has wheel */
"No wheels detected");
} else {
"Wheel detected");
}
/* get the data format from the hid descriptor */
qprocsoff(q);
return (EINVAL);
}
"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,
{
"usbms_close entering");
qprocsoff(q);
if (usbmsp->usbms_jitter) {
(void) quntimeout(q,
usbmsp->usbms_jitter = 0;
}
if (usbmsp->usbms_reioctl_id) {
usbmsp->usbms_reioctl_id = 0;
}
if (usbmsp->usbms_resched_id) {
usbmsp->usbms_resched_id = 0;
}
/*
* 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.
*/
}
/* Free mouse buffer */
}
"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)
{
struct usbmousebuf *b;
struct usbmouseinfo *mi;
"usbms_rserv entering");
switch (ms->ms_readformat) {
case MS_3BYTE_FORMAT: {
register char *cp;
"Can't set to 3 byte format. Length != 1");
return;
}
/* Update read buttons */
/* lower pri to avoid mouse droppings */
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
}
(size_t)3,
(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? */
}
/* circular buffer wraparound */
}
break;
}
case MS_VUID_FORMAT:
default: {
do {
switch (ms->ms_eventstate) {
case EVENT_WHEEL:
1 : 0);
if (usbmsp->usbms_num_wheels) {
for (i = 0; i < loop; 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 */
break;
case EVENT_Y:
break;
case EVENT_X:
break;
default:
/* start again */
break;
}
/* lower pri to avoid mouse droppings */
}
/* circular buffer wraparound */
}
} else
ms->ms_eventstate--;
}
}
}
"usbms_rserv exiting");
}
/*
* usbms_rserv_vuid_event_wheel
* convert wheel data to firm events
*/
static void
struct usbmouseinfo *mi,
{
return;
}
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
}
(void (*)())usbms_resched, (void *) usbmsp);
if (usbmsp->usbms_resched_id == 0) {
/* try again later */
return;
}
/* flush the queue */
}
}
}
/*
* usbms_rserv_vuid_button() :
* Process a VUID button event
*/
static void
struct usbmouseinfo *mi,
{
int button_number;
/* Test button. Send an event if it changed. */
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 */
ms->ms_eventstate);
break;
}
ms->ms_vuidaddr) |
+ button_number);
/*
* Update read buttons and set
* value
*/
ms->ms_prevbuttons |=
} else {
ms->ms_prevbuttons &=
~hwbit;
}
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
}
qbufcall(q,
sizeof (Firm_event),
(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? */
}
}
}
/*
* 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,
{
register Firm_event *fep;
/*
* 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).
*/
return;
}
}
/* Send y if changed. */
ms->ms_vuidaddr) |
} else {
ms->ms_vuidaddr) |
usbmsp->usbms_logical_Ymax) >=
}
}
} else {
if (usbmsp->usbms_resched_id) {
qunbufcall(q,
}
qbufcall(q,
sizeof (Firm_event),
(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? */
}
}
}
/*
* 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,
{
register Firm_event *fep;
/*
* 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).
*/
return;
}
}
/* Send x if changed. */
ms->ms_vuidaddr) |
} else {
usbmsp->usbms_logical_Xmax) >=
}
}
} else {
if (usbmsp->usbms_resched_id)
qunbufcall(q,
qbufcall(q,
sizeof (Firm_event),
(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? */
}
}
}
/*
* 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;
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,
{
"usbms_wput entering");
case M_FLUSH: /* Canonical flush handling */
}
}
break;
case M_IOCTL:
usbms_ioctl(q, mp);
break;
case M_IOCDATA:
usbms_miocdata(q, mp);
break;
default:
}
"usbms_wput exiting");
return (0);
}
/*
* usbms_ioctl() :
* Process ioctls we recognize and own. Otherwise, NAK.
*/
static void
usbms_ioctl(register queue_t *q,
{
int err = 0;
ushort_t transparent = 0;
"usbms_ioctl entering");
return;
}
case VUIDSFORMAT:
if (err != 0)
break;
break;
}
/*
* Flush mouse buffer because the messages upstream of us
* are in the old format.
*/
break;
case VUIDGFORMAT:
ioctlrespsize = sizeof (int);
goto allocfailure;
}
break;
case VUIDGADDR:
case VUIDSADDR:
if (err != 0)
break;
break;
}
else
break;
case MSIOGETPARMS:
ioctlrespsize = sizeof (Ms_parms);
goto allocfailure;
}
break;
case MSIOSETPARMS:
if (err != 0)
break;
break;
case MSIOBUTTONS:
ioctlrespsize = sizeof (int);
goto allocfailure;
}
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 */
transparent = 1;
0, M_COPYOUT)) {
break;
}
}
ioctlrespsize = sizeof (int);
goto allocfailure;
}
}
if (transparent) {
return;
}
break;
case VUIDGWHEELINFO:
sizeof (usbms_iocstate_t),
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.
*/
return;
}
sizeof (wheel_info)) {
break;
}
break;
case VUIDGWHEELSTATE:
sizeof (usbms_iocstate_t),
sizeof (wheel_state),
0,
M_COPYIN)) {
break;
}
return;
}
break;
}
break;
case VUIDSWHEELSTATE:
sizeof (usbms_iocstate_t),
sizeof (wheel_state),
0,
M_COPYIN)) {
break;
}
return;
}
break;
}
break;
case MSIOSRESOLUTION:
sizeof (usbms_iocstate_t),
sizeof (Ms_screen_resolution),
0,
M_COPYIN)) {
break;
}
return;
}
break;
}
break;
default:
return;
} /* switch */
if (err != 0)
else {
}
return;
/*
* 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.
*/
if (usbmsp->usbms_reioctl_id) {
}
(void (*)())usbms_reioctl,
(void *)usbmsp);
}
/*
* M_IOCDATA processing for IOCTL's: VUIDGWHEELCOUNT, VUIDGWHEELINFO,
* VUIDGWHEELSTATE, VUIDSWHEELSTATE & MSIOSRESOLUTION.
*/
static void
usbms_miocdata(register queue_t *q,
{
int err = 0;
goto err;
}
case VUIDGWHEELCOUNT:
break;
case VUIDGWHEELINFO:
break;
}
goto err;
}
sizeof (wheel_info), 0, M_COPYOUT)) {
goto err;
}
}
break;
case VUIDGWHEELSTATE:
break;
}
VUIDGWHEELSTATE)) {
goto err;
}
sizeof (wheel_state), 0, M_COPYOUT)) {
goto err;
}
}
break;
case VUIDSWHEELSTATE:
break;
}
VUIDSWHEELSTATE)) {
goto err;
}
break;
case MSIOSRESOLUTION:
break;
}
goto err;
}
break;
default:
break;
}
err:
if (err) {
}
if (copyresp->cp_private) {
}
}
}
/*
* 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)
{
register queue_t *q;
q = usbmsp->usbms_wq_ptr;
usbms_ioctl(q, mp);
}
}
/*
* usbms_getparms() :
* Called from MSIOGETPARMS ioctl to get the
* current jitter_thesh, speed_law and speed_limit
* values.
*/
static int
{
return (0);
}
/*
* usbms_setparms() :
* Called from MSIOSETPARMS ioctl to set the
* current jitter_thesh, speed_law and speed_limit
* values.
*/
static int
{
return (0);
}
/*
* usbms_flush() :
* Resets the ms_softc structure to default values
* and sends M_FLUSH above.
*/
static void
{
register queue_t *q;
"usbms_flush entering");
}
"usbms_flush exiting");
}
/*
* usbms_rput() :
* Put procedure for input from driver end of stream (read queue).
*/
static void
usbms_rput(queue_t *q,
{
/* Maintain the original mp */
if (usbmsp == 0) {
return;
}
case M_FLUSH:
return;
case M_BREAK:
/*
* We don't have to handle this
* because nothing is sent from the downstream
*/
return;
case M_DATA:
return;
}
break;
case M_CTL:
usbms_mctl_receive(q, mp);
return;
case M_ERROR:
return;
default:
return;
}
/*
* A data message, consisting of bytes from the mouse.
* Make sure there are atleast "limit" number of bytes.
*/
return;
}
do {
return;
} else {
/* We skip the report id prefix. */
}
}
}
/*
* 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,
{
case HID_GET_PARSER_HANDLE:
*(hidparser_handle_t *)data;
} else {
}
break;
case HID_SET_PROTOCOL:
/* FALLTHRU */
default:
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
{
register struct usbmousebuf *b;
register struct usbmouseinfo *mi;
register int jitter_radius;
register int temp = 0;
ushort_t i;
char c;
char cxyz = 0; /* for one byte */
short sxyz = 0; /* for two bytes */
int ixyz = 0; /* for four bytes */
"usbms_input entering");
if (b == NULL) {
return;
}
/*
* Lower 3 bits are middle, right, left.
*/
"left button pressed");
}
"right button pressed");
}
"middle button pressed");
}
if (nbutt > 3) {
if (c & USBMS_BUT(i)) {
USB_BUT_PRESSED(i);
"%d button pressed", i);
}
}
}
/*
* According to the length of the X coordinate,
* the current delta X is got from the sample.
*/
case 1:
break;
case 2:
for (i = 0; i < 2; i++) {
+ i] << (8 * i));
}
break;
case 4:
for (i = 0; i < 4; i++) {
+ i] << (8 * i));
}
break;
}
} else {
}
/*
* According to the length of the Y coordinate,
* the current delta Y is got from the sample.
*/
case 1:
break;
case 2:
for (i = 0; i < 2; i++) {
+ i] << (8 * i));
}
break;
case 4:
for (i = 0; i < 4; i++) {
+ i] << (8 * i));
}
break;
}
} else {
}
/*
* Check the wheel data in the current event.
* If it exists, the wheel data is got from the sample.
*/
if (usbmsp->usbms_num_wheels) {
case 1:
break;
case 2:
for (i = 0; i < 2; i++) {
+ i] << (8 * i));
}
break;
case 4:
for (i = 0; i < 4; i++) {
+ i] << (8 * i));
}
break;
}
} else {
}
}
if (usbmsp->usbms_jitter) {
usbmsp->usbms_jitter = 0;
}
if (!usbmsp->usbms_num_wheels) {
}
/*
* If there is a wheel movement or a change in the button state,
* send the data up immediately.
*/
/*
* Buttons did not change; did position?
*/
/* no, position did not change */
return;
}
/*
* Did the mouse move more than the jitter threshhold?
*/
/*
* 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.
*/
return;
}
}
"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)
{
register struct usbmousebuf *b;
register struct usbmouseinfo *mi;
register int wake;
/*
* No longer waiting for jitter timeout
*/
usbmsp->usbms_jitter = 0;
"usbms_incr entering");
if (b == NULL) {
return;
}
if (usbmsp->usbms_speedlaw) {
}
}
}
}
/* See if we need to wake up anyone waiting for input */
/* Adjust circular buffer pointer */
b->mb_off = 0;
} else {
mi++;
}
/*
* If over-took read index then flush buffer so that mouse state
* is consistent.
*/
if (overrun_msg) {
"Mouse buffer flushed when overrun.");
}
overrun_cnt++;
}
/* Remember current buttons and fractional part of x & y */
if (wake) {
"usbms_incr run service");
}
"usbms_incr exiting");
}
/*
* usbms_check_for_wheels
* return SUCCESS if wheel is found, else return FAILURE
*/
static int
{
if (usbmsp->usbms_report_descr_handle) {
/* Get the report id that has mouse data */
0, /* Doesn't matter */
report_id = 0;
} else {
}
/* find no. of wheels in this report */
if (rval == HIDPARSER_SUCCESS) {
/*
* Found wheel. By default enable the wheel.
* Currently only enable only the first wheel.
*/
return (USB_SUCCESS);
}
}
usbmsp->usbms_num_wheels = 0;
return (USB_FAILURE);
}
/*
* usbms_make_copyreq
* helper function for usbms ioctls
*/
static int
{
}
return (EINVAL);
}
if (pvtsize) {
return (EAGAIN);
}
}
if (state) {
if (pvtsize) {
} else {
}
}
if (contsize) {
return (EAGAIN);
}
}
}
return (USB_SUCCESS);
}
static int
{
wheel_info *wi;
return (err);
}
return (err);
}
return (USB_SUCCESS);
}
static int
usbms_service_wheel_state(register queue_t *q,
{
return (err);
}
return (err);
}
switch (cmd) {
case VUIDGWHEELSTATE:
break;
case VUIDSWHEELSTATE:
break;
default:
return (err);
}
return (USB_SUCCESS);
}
/*
* usbms_get_screen_parms() :
* Called from MSIOSRESOLUTION ioctl to get the
*/
static int
usbms_get_screen_parms(register queue_t *q,
{
return (USB_SUCCESS);
}
static void
{
}
}
/*
* 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
{
uint_t i, j, button_page;
register queue_t *q;
int rval;
/* allocate hidparser report structure */
/*
* Check what is the total length of the mouse packet
* and get the usages and their lengths in order
*/
ms_rpt);
if (rval != HIDPARSER_SUCCESS) {
return (USB_FAILURE);
}
for (i = 0; i < ms_rpt->no_of_usages; i++) {
if (rptcnt == 0) {
continue;
}
button_page = 0;
for (j = 0; j < rptcnt; j++) {
} else {
}
HID_GD_Y) {
} else {
}
HID_GD_WHEEL) {
} else {
}
}
button_page = 1;
break;
}
}
i += j;
/* enter the next valid usage */
if (button_page == 0) {
i --;
}
}
/* get the length of sending data */
/* Check whether X and Y are relative or absolute */
if (rval != HIDPARSER_SUCCESS) {
return (USB_FAILURE);
}
/* For the time being assume that Y also has the same attr */
/* get the logical_maximum for X and Y respectively */
/* the data format can't be parsed correctly */
if (limit % 8) {
"Wrong data packet include %d bits", limit);
return (USB_FAILURE);
}
"fail to get X logical max.");
return (USB_FAILURE);
}
"fail to get Y logical max.");
return (USB_FAILURE);
}
if (usbmsp->usbms_logical_Xmax == 0) {
"X logical max value is zero");
return (USB_FAILURE);
}
if (usbmsp->usbms_logical_Ymax == 0) {
"Y logical max value is zero");
return (USB_FAILURE);
}
/* The wheel is not supported in current remote kvms. */
usbmsp->usbms_num_wheels = 0;
NULL) {
return (USB_NO_RESOURCES);
} else {
/*
* notify the upper that it is an absolute mouse
*/
q = usbmsp->usbms_rq_ptr;
}
} else {
/* general data format of relative mouse */
/* three-byte packet for general mouse */
if (limit % 8) {
"Data per packet include %d bits", limit);
}
}
return (USB_SUCCESS);
}