hid.c revision ac9468f8dd2abd7271cca0d9e1b15831d367cf9f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Human Interface Device driver (HID)
*
* The HID driver is a software driver which acts as a class
* driver for USB human input devices like keyboard, mouse,
* joystick etc and provides the class-specific interfaces
* between these client driver modules and the Universal Serial
* Bus Driver(USBA).
*
* NOTE: This driver is not DDI compliant in that it uses undocumented
* functions for logging (USB_DPRINTF_L*, usb_alloc_log_hdl, usb_free_log_hdl).
*
* Undocumented functions may go away in a future Solaris OS release.
*
* Please see the DDK for sample code of these functions, and for the usbskel
* skeleton template driver which contains scaled-down versions of these
* functions written in a DDI-compliant way.
*/
#define USBDRV_MAJOR_VER 2
#define USBDRV_MINOR_VER 0
/* Debugging support */
/* tunables */
int hid_pm_mouse = 0;
/* soft state structures */
#define HID_INITIAL_SOFT_SPACE 4
static void *hid_statep;
/* Callbacks */
static void hid_interrupt_pipe_callback(usb_pipe_handle_t,
usb_intr_req_t *);
static void hid_interrupt_pipe_exception_callback(usb_pipe_handle_t,
usb_intr_req_t *);
static void hid_default_pipe_exception_callback(usb_pipe_handle_t,
usb_ctrl_req_t *);
static int hid_restore_state_event_callback(dev_info_t *);
static int hid_disconnect_event_callback(dev_info_t *);
/* Supporting routines */
usb_alt_if_data_t *, usb_ep_data_t *);
static int hid_parse_hid_descr_failure(hid_state_t *);
static int hid_handle_report_descriptor(hid_state_t *, int);
static void hid_set_idle(hid_state_t *);
static void hid_set_protocol(hid_state_t *, int);
static int hid_start_intr_polling(hid_state_t *);
static void hid_close_intr_pipe(hid_state_t *);
mblk_t *);
static int hid_is_pm_enabled(dev_info_t *);
static void hid_save_device_state(hid_state_t *);
static int hid_pwrlvl0(hid_state_t *);
static int hid_pwrlvl1(hid_state_t *);
static int hid_pwrlvl2(hid_state_t *);
static int hid_pwrlvl3(hid_state_t *);
static void hid_pm_busy_component(hid_state_t *);
static void hid_pm_idle_component(hid_state_t *);
static int hid_polled_input_enter(hid_polled_handle_t);
static int hid_polled_input_exit(hid_polled_handle_t);
static int hid_polled_input_init(hid_state_t *);
static int hid_polled_input_fini(hid_state_t *);
/* Streams entry points */
/* dev_ops entry points */
static int hid_power(dev_info_t *, int, int);
/*
* Warlock is not aware of the automatic locking mechanisms for
* streams drivers. The hid streams enter points are protected by
* a per module perimeter. If the locking in hid is a bottleneck
* per queue pair or per queue locking may be used. Since warlock
* is not aware of the streams perimeters, these notes have been added.
*
* Note that the perimeters do not protect the driver from callbacks
* happening while a streams entry point is executing. So, the hid_mutex
* has been created to protect the data.
*/
/* module information */
static struct module_info hid_mod_info = {
0x0ffff, /* module id number */
"hid", /* module name */
0, /* min packet size accepted */
INFPSZ, /* max packet size accepted */
512, /* hi-water mark */
128 /* lo-water mark */
};
/* read queue information structure */
NULL, /* put procedure not needed */
NULL, /* service procedure not needed */
hid_open, /* called on startup */
hid_close, /* called on finish */
NULL, /* for future use */
&hid_mod_info, /* module information structure */
NULL /* module statistics structure */
};
/* write queue information structure */
hid_wput, /* put procedure */
hid_wsrv, /* service procedure */
NULL, /* open not used on write side */
NULL, /* close not used on write side */
NULL, /* for future use */
&hid_mod_info, /* module information structure */
NULL /* module statistics structure */
};
struct streamtab hid_streamtab = {
&rinit,
&winit,
NULL, /* not a MUX */
NULL /* not a MUX */
};
struct cb_ops hid_cb_ops = {
nulldev, /* open */
nulldev, /* close */
nulldev, /* strategy */
nulldev, /* print */
nulldev, /* dump */
nulldev, /* read */
nulldev, /* write */
nulldev, /* ioctl */
nulldev, /* devmap */
nulldev, /* mmap */
nulldev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
&hid_streamtab, /* streamtab */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
hid_info, /* info */
nulldev, /* identify */
nulldev, /* probe */
hid_attach, /* attach */
hid_detach, /* detach */
nodev, /* reset */
&hid_cb_ops, /* driver operations */
NULL, /* bus operations */
hid_power, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
static struct modldrv hidmodldrv = {
"USB HID Client Driver",
&hid_ops /* driver ops */
};
static struct modlinkage modlinkage = {
NULL,
};
static usb_event_t hid_events = {
NULL,
NULL,
};
int
_init(void)
{
int rval;
HID_INITIAL_SOFT_SPACE)) != 0)) {
return (rval);
}
}
return (rval);
}
int
_fini(void)
{
int rval;
return (rval);
}
return (rval);
}
int
{
}
/*
* hid_info :
* Get minor number, soft state structure etc.
*/
/*ARGSUSED*/
static int
{
int error = DDI_FAILURE;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
}
} else
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
/*
* hid_attach :
* Gets called at the time of attach. Do allocation,
* and initialization of the software structure.
* Get all the descriptors, setup the
* report descriptor tree by calling hidparser
* function.
*/
static int
{
int parse_hid_descr_error = 0;
char minor_name[HID_MINOR_NAME_LEN];
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Allocate softstate information and get softstate pointer
*/
}
goto fail;
}
&hid_errmask, &hid_instance_debug, 0);
/*
* Register with USBA. Just retrieve interface descriptor
*/
"hid_attach: client attach failed");
goto fail;
}
USB_SUCCESS) {
"hid_attach: usb_get_dev_data() failed");
goto fail;
}
/* initialize mutex */
/* get interface data for alternate 0 */
/*
* Make sure that the bInterfaceProtocol only has meaning to
* Boot Interface Subclass.
*/
hidp->hid_interfaceno, 0, 0,
"no interrupt IN endpoint found");
goto fail;
}
/*
* Attempt to find the hid descriptor, it could be after interface
* or after endpoint descriptors
*/
/*
* If parsing of hid descriptor failed and
* the device is a keyboard or mouse, use predefined
* length and packet size.
*/
goto fail;
}
/*
* hid descriptor was bad but since
* the device is a keyboard or mouse,
* we will use the default length
* and packet size.
*/
} else {
/* Parse hid descriptor successful */
"Hid descriptor:\n\t"
"bLength = 0x%x bDescriptorType = 0x%x "
"bcdHID = 0x%x\n\t"
"bCountryCode = 0x%x bNumDescriptors = 0x%x\n\t"
"bReportDescriptorType = 0x%x\n\t"
"wReportDescriptorLength = 0x%x",
}
/*
* Save a copy of the default pipe for easy reference
*/
/* we copied the descriptors we need, free the dev_data */
/*
* Don't get the report descriptor if parsing hid descriptor earlier
* failed since device probably won't return valid report descriptor
* either. Though parsing of hid descriptor failed, we have reached
* this point because the device has been identified as a
* keyboard or a mouse successfully and the default packet
* size and layout(in case of keyboard only) will be used, so it
* is ok to go ahead even if parsing of hid descriptor failed and
* we will not try to get the report descriptor.
*/
if (parse_hid_descr_error != HID_BAD_DESCR) {
/*
* Sun mouse rev 105 is a bit slow in responding to this
* request and requires multiple retries
*/
int retry;
/*
* Get and parse the report descriptor.
* Set the packet size if parsing is successful.
* Note that we start retry at 1 to have a delay
* in the first iteration.
*/
break;
}
}
goto fail;
}
/*
* If packet size is zero, but the device is identified
* as a mouse or a keyboard, use predefined packet
* size.
*/
if (hidp->hid_packet_size == 0) {
/* device is a keyboard */
} else if (hidp->
/* device is a mouse */
} else {
"Failed to find hid packet size");
goto fail;
}
}
}
/*
* initialize the pipe policy for the interrupt pipe.
*/
/*
* Make a clas specific request to SET_IDLE
* In this case send no reports if state has not changed.
* See HID 7.2.4.
*/
/* always initialize to report protocol */
/*
* Create minor node based on information from the
* descriptors
*/
case KEYBOARD_PROTOCOL:
break;
case MOUSE_PROTOCOL:
break;
default:
/*
* If the report descriptor has the GD mouse collection in
* its multiple collection, create a minor node and support it.
*/
HID_GD_MOUSE) != HIDPARSER_FAILURE) {
break;
}
switch (usage_page) {
case HID_CONSUMER:
switch (usage) {
case HID_CONSUMER_CONTROL:
(void) strcpy(minor_name,
"consumer_control");
break;
default:
(void) sprintf(minor_name,
break;
}
break;
case HID_GENERIC_DESKTOP:
switch (usage) {
case HID_GD_POINTER:
(void) strcpy(minor_name,
"pointer");
break;
case HID_GD_MOUSE:
(void) strcpy(minor_name,
"mouse");
break;
case HID_GD_KEYBOARD:
(void) strcpy(minor_name,
"keyboard");
break;
default:
(void) sprintf(minor_name,
break;
}
break;
default:
(void) sprintf(minor_name,
break;
}
} else {
"hid_attach: Unsupported HID device");
goto fail;
}
break;
}
DDI_PSEUDO, 0)) != DDI_SUCCESS) {
"hid_attach: Could not create minor node");
goto fail;
}
/* create internal path for virtual */
goto fail;
}
}
DDI_SUCCESS) {
goto fail;
}
}
/* register for all events */
"usb_register_event_cbs failed");
goto fail;
}
/* now create components to power manage this device */
/*
* report device
*/
"hid_attach: End");
return (DDI_SUCCESS);
fail:
if (hidp) {
"hid_attach: fail");
}
return (DDI_FAILURE);
}
/*
* hid_detach :
* Gets called at the time of detach.
*/
static int
{
int rval = DDI_FAILURE;
switch (cmd) {
case DDI_DETACH:
/*
* Undo what we did in client_attach, freeing resources
* and removing things we installed. The system
* framework guarantees we are not active with this devinfo
* node in any other entry points at this time.
*/
return (DDI_SUCCESS);
case DDI_SUSPEND:
default:
break;
}
return (rval);
}
/*
* hid_open :
* Open entry point: Opens the interrupt pipe. Sets up queues.
*/
/*ARGSUSED*/
static int
{
int no_of_ep = 0;
int rval;
int instance;
return (ENXIO);
}
"hid_open: Begin");
if (sflag) {
/* clone open NOT supported here */
return (ENXIO);
}
return (EIO);
}
/*
* This is a workaround:
* Currently, if we open an already disconnected device, and send
* a CONSOPENPOLL ioctl to it, the system will panic, please refer
* to the processing HID_OPEN_POLLED_INPUT ioctl in the routine
* hid_mctl_receive().
* The consconfig_dacf module need this interface to detect if the
* device is already disconnnected.
*/
return (ENODEV);
}
if (q == tmpq->hidq_queue) {
return (0);
} else {
return (EBUSY);
}
}
}
/*
* Add this queue to the head of the queue list. Only the list
* head (active queue) gets input. Other (older) queues will
* be activated after the (current) active one is closed.
*/
hidq->hidq_queue = q;
/* just return in case that pipes already open */
/*
* Two queues are supported by now:
* one external (aka. physical) and one virtual (aka. internal)
*/
qprocson(q);
return (0);
}
/* Check if interrupt endpoint exists */
if (no_of_ep > 0) {
/* Open the interrupt pipe */
&hidp->hid_interrupt_pipe) !=
USB_SUCCESS) {
return (EIO);
}
}
qprocson(q);
"unable to start intr pipe polling. rval = %d", rval);
qprocsoff(q);
return (EIO);
}
/*
* Keyboard and mouse is Power managed by device activity.
* All other devices go busy on open and idle on close.
*/
case HID_PM_ACTIVITY:
break;
default:
break;
}
return (0);
}
/*
* hid_close :
* Close entry point.
*/
/*ARGSUSED*/
static int
{
int str_flags;
/*
* In case there are any outstanding requests on
* the default pipe, wait forever for them to complete.
*/
/* drain any M_CTLS on the WQ */
}
qprocsoff(q);
if (hidq->hidq_queue == q) {
break;
}
}
} else {
}
/* just return in case that any queue is active */
if (hidp->hid_queue_list) {
return (0);
}
/* all queues are closed, close USB pipes */
/*
*/
case HID_PM_ACTIVITY:
break;
default:
break;
}
"hid_close: End");
return (0);
}
/*
* hid_wput :
* write put routine for the hid module
*/
static int
{
int error = USB_SUCCESS;
"hid_wput: Begin");
/* See if the upper module is passing the right thing */
case M_FLUSH: /* Canonical flush handling */
}
/* read queue not used so just send up */
} else {
}
break;
case M_IOCTL:
break;
case M_CTL:
/* we are busy now */
if (q->q_first) {
} else {
switch (error) {
case HID_ENQUEUE:
/*
* put this mblk on the WQ for the wsrv to
* process
*/
break;
case HID_INPROGRESS:
/* request has been queued to the device */
break;
case HID_SUCCESS:
/*
* returned by M_CTLS that are processed
* immediately
*/
/* FALLTHRU */
case HID_FAILURE:
default:
break;
}
}
break;
default:
error = USB_FAILURE;
break;
}
"hid_wput: End");
return (DDI_SUCCESS);
}
/*
* hid_wsrv :
* Write service routine for hid. When a message arrives through
* hid_wput(), it is kept in write queue to be serviced later.
*/
static int
{
int error;
"hid_wsrv: Begin");
"hid_wsrv: dev_state: %s",
/*
* raise power if we are powered down. It is OK to block here since
* we have a separate thread to process this STREAM
*/
}
/*
* continue servicing all the M_CTL's till the queue is empty
* or the device gets disconnected or till a hid_close()
*/
/* Send a message down */
switch (error) {
case HID_ENQUEUE:
/* put this mblk back on q to preserve order */
break;
case HID_INPROGRESS:
/* request has been queued to the device */
break;
case HID_SUCCESS:
case HID_FAILURE:
default:
break;
}
}
"hid_wsrv: End");
return (DDI_SUCCESS);
}
/*
* hid_power:
* power entry point
*/
static int
{
int retval;
/* check if we are transitioning to a legal power level */
"hid_power: illegal level=%d hid_pwr_states=%d",
return (DDI_FAILURE);
}
switch (level) {
case USB_DEV_OS_PWR_OFF:
break;
case USB_DEV_OS_PWR_1:
break;
case USB_DEV_OS_PWR_2:
break;
case USB_DEV_OS_FULL_PWR:
break;
default:
break;
}
}
/*
* hid_interrupt_pipe_callback:
* Callback function for the hid intr pipe. This function is called by
* USBA when a buffer has been filled. This driver does not cook the data,
* it just sends the message up.
*/
static void
{
queue_t *q;
"hid_interrupt_pipe_callback: ph = 0x%p req = 0x%p",
/*
* If hid_close() is in progress, we shouldn't try accessing queue
* Otherwise indicate that a putnext is going to happen, so
* if close after this, that should wait for the putnext to finish.
*/
/*
* Check if data can be put to the next queue.
*/
"Buffer flushed when overflowed.");
/* Flush the queue above */
} else {
/* Put data upstream */
/* usb_free_intr_req should not free data */
}
} else {
}
/* free request and data */
}
/*
* hid_default_pipe_callback :
* Callback routine for the asynchronous control transfer
* Called from hid_send_async_ctrl_request() where we open
* the pipe in exclusive mode
*/
static void
{
"hid_default_pipe_callback: "
"ph = 0x%p, req = 0x%p, data= 0x%p",
}
/*
* Free the b_cont of the original message that was sent down.
*/
/* chain the mblk received to the original & send it up */
if (canputnext(rq)) {
} else {
}
/*
* Free the argument for the asynchronous callback
*/
/*
* Free the control pipe request structure.
*/
}
/*
* hid_interrupt_pipe_exception_callback:
* Exception callback routine for interrupt pipe. If there is any data,
* destroy it. No threads are waiting for the exception callback.
*/
/*ARGSUSED*/
static void
{
int rval;
"hid_interrupt_pipe_exception_callback: "
"completion_reason = 0x%x, data = 0x%p, flag = 0x%x",
if (((flags & USB_CB_FUNCTIONAL_STALL) != 0) &&
((flags & USB_CB_STALL_CLEARED) == 0)) {
"hid_interrupt_pipe_exception_callback: "
"unable to clear stall. flags = 0x%x",
req->intr_cb_flags);
}
switch (req->intr_completion_reason) {
case USB_CR_STOPPED_POLLING:
case USB_CR_PIPE_CLOSING:
default:
break;
case USB_CR_PIPE_RESET:
case USB_CR_NO_RESOURCES:
USB_SUCCESS)) {
"unable to restart interrupt poll. rval = %d",
rval);
}
break;
}
}
/*
* hid_default_pipe_exception_callback:
* Exception callback routine for default pipe.
*/
/*ARGSUSED*/
static void
{
"hid_default_pipe_exception_callback: "
"completion_reason = 0x%x, data = 0x%p, flag = 0x%x",
/*
* Pass an error message up. Reuse existing mblk.
*/
if (canputnext(rq)) {
} else {
}
}
/*
* event handling:
*
* hid_reconnect_event_callback:
* the device was disconnected but this instance not detached, probably
* because the device was busy
*
* If the same device, continue with restoring state
*/
static int
{
"hid_restore_state_event_callback: dip=0x%p", (void *)dip);
return (USB_SUCCESS);
}
/*
* hid_cpr_suspend
* Fail suspend if we can't finish outstanding i/o activity.
*/
static int
{
int rval, prev_state;
int retval = USB_FAILURE;
switch (hidp->hid_dev_state) {
case USB_DEV_ONLINE:
case USB_DEV_PWRED_DOWN:
case USB_DEV_DISCONNECTED:
/* drain all request outstanding on the default control pipe */
USB_FLAGS_SLEEP, NULL, 0);
/* fail checkpoint if we haven't finished the job yet */
"hid_cpr_suspend: "
"device busy - can't checkpoint");
/* fall back to previous state */
} else {
}
break;
case USB_DEV_SUSPENDED:
default:
"hid_cpr_suspend: Illegal dev state: %d",
break;
}
return (retval);
}
static void
{
}
/*
* hid_disconnect_event_callback:
* The device has been disconnected. We either wait for
* detach or a reconnect event. Close all pipes and timeouts.
*/
static int
{
"hid_disconnect_event_callback: dip=0x%p", (void *)dip);
switch (hidp->hid_dev_state) {
case USB_DEV_ONLINE:
case USB_DEV_PWRED_DOWN:
"busy device has been disconnected");
}
/*
* Notify applications about device removal, this only
* applies to an external (aka. physical) open. For an
* internal open, consconfig_dacf closes the queue.
*/
}
}
}
break;
case USB_DEV_SUSPENDED:
/* we remain suspended */
break;
default:
"hid_disconnect_event_callback: Illegal dev state: %d",
break;
}
return (USB_SUCCESS);
}
/*
* hid_power_change_callback:
* Async callback function to notify pm_raise_power completion
* after hid_power entry point is called.
*/
static void
{
"hid_power_change_callback - rval: %d", rval);
} else {
}
}
/*
* hid_parse_hid_descr:
* Parse the hid descriptor, check after interface and after
* endpoint descriptor
*/
static size_t
{
int which_cvs;
continue;
}
return (usb_parse_data("ccscccs",
(void *)ret_descr,
(size_t)ret_buf_len));
}
}
/* now try after endpoint */
continue;
}
return (usb_parse_data("ccscccs",
(void *)ret_descr,
(size_t)ret_buf_len));
}
}
return (USB_PARSE_ERROR);
}
/*
* hid_parse_hid_descr_failure:
* If parsing of hid descriptor failed and the device is
* a keyboard or mouse, use predefined length and packet size.
*/
static int
{
/*
* Parsing hid descriptor failed, probably because the
* device did not return a valid hid descriptor. Check to
* see if this is a keyboard or mouse. If so, use the
* predefined hid descriptor length and packet size.
* Otherwise, detach and return failure.
*/
"Parsing of hid descriptor failed");
"Set hid descriptor length to predefined "
"USB_KB_HID_DESCR_LENGTH for keyboard.");
/* device is a keyboard */
"Set hid descriptor length to predefined "
"USB_MS_HID_DESCR_LENGTH for mouse.");
/* device is a mouse */
} else {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* hid_handle_report_descriptor:
* Get the report descriptor, call hidparser routine to parse
* it and query the hidparser tree to get the packet size
*/
static int
int interface)
{
int i;
USB_DEV_REQ_DEV_TO_HOST | /* bmRequestType */
USB_REQ_GET_DESCR, /* bRequest */
USB_CLASS_DESCR_TYPE_REPORT, /* wValue */
0, /* wIndex: interface, fill in later */
0, /* wLength, fill in later */
0 /* attributes */
};
/*
* Parsing hid desciptor was successful earlier.
* Get Report Descriptor
*/
&setup,
&data, /* data */
"Failed to receive the Report Descriptor");
return (USB_FAILURE);
} else {
/* Print the report descriptor */
for (i = 0; i < n; i++) {
"Index = %d\tvalue =0x%x", i,
}
/* Get Report Descriptor was successful */
/* find max intr-in xfer length */
/* round up to the nearest byte */
/* if report id is used, add more more byte for it */
hidp->hid_packet_size++;
}
} else {
"Invalid Report Descriptor");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
}
/*
* hid_set_idle:
* Make a clas specific request to SET_IDLE.
* In this case send no reports if state has not changed.
* See HID 7.2.4.
*/
/*ARGSUSED*/
static void
{
USB_DEV_REQ_HOST_TO_DEV | /* bmRequestType */
SET_IDLE, /* bRequest */
DURATION, /* wValue */
0, /* wIndex: interface, fill in later */
0, /* wLength */
0 /* attributes */
};
"hid_set_idle: Begin");
&setup,
NULL, /* no data to send. */
"Failed while trying to set idle,"
"cr = %d, cb_flags = 0x%x\n",
}
"hid_set_idle: End");
}
/*
* hid_set_protocol:
* Initialize the device to set the preferred protocol
*/
/*ARGSUSED*/
static void
{
"hid_set_protocol(%d): Begin", protocol);
/* initialize the setup request */
&setup,
NULL, /* no data to send */
/*
* Some devices fail to follow the specification
* and instead of STALLing, they continously
* NAK the SET_IDLE command. We need to reset
* the pipe then, so that ohci doesn't panic.
*/
"Failed while trying to set protocol:%d,"
"cr = %d cb_flags = 0x%x\n",
}
"hid_set_protocol: End");
}
/*
* hid_detach_cleanup:
* called by attach and detach for cleanup.
*/
static void
{
int rval;
"hid_detach_cleanup: Begin");
goto done;
}
/*
* Disable the event callbacks first, after this point, event
* callbacks will never get called. Note we shouldn't hold
* mutex while unregistering events because there may be a
* competing event callback thread. Event callbacks are done
* with ndi mutex held and this can cause a potential deadlock.
*/
"hid_detach_cleanup: hidpm=0x%p", (void *)hidpm);
if (hidpm->hid_wakeup_enabled) {
/* First bring the device to full power */
(void) pm_raise_power(dip, 0,
/* Disable remote wakeup */
if (rval != DDI_SUCCESS) {
"hid_detach_cleanup: "
"disble remote wakeup failed, "
"rval= %d", rval);
}
}
}
}
if (hidpm) {
}
}
if (flags & HID_MINOR_NODES) {
}
"hid_detach_cleanup: End");
done:
}
/*
* hid_start_intr_polling:
* Allocate an interrupt request structure, initialize,
* and start interrupt transfers.
*/
static int
{
int rval = USB_SUCCESS;
"hid_start_intr_polling: "
"dev_state=%s str_flags=%d ph=0x%p",
(void *)hidp->hid_interrupt_pipe);
/*
* initialize interrupt pipe request structure
*/
/*
* Start polling on the interrupt pipe.
*/
USB_FLAGS_SLEEP)) != USB_SUCCESS) {
"hid_start_intr_polling failed: rval = %d",
rval);
}
}
"hid_start_intr_polling: done, rval = %d", rval);
return (rval);
}
/*
* hid_close_intr_pipe:
* close the interrupt pipe after draining all callbacks
*/
static void
{
"hid_close_intr_pipe: Begin");
if (hidp->hid_interrupt_pipe) {
/*
* Close the interrupt pipe
*/
}
"hid_close_intr_pipe: End");
}
/*
* hid_mctl_receive:
* Handle M_CTL messages from upper stream. If
* we don't understand the command, free message.
*/
static int
{
int error = HID_FAILURE;
"hid_mctl_receive");
case HID_SET_REPORT:
/* FALLTHRU */
case HID_SET_IDLE:
/* FALLTHRU */
case HID_SET_PROTOCOL:
break;
case HID_GET_REPORT:
/* FALLTHRU */
case HID_GET_IDLE:
/* FALLTHRU */
case HID_GET_PROTOCOL:
break;
case HID_GET_PARSER_HANDLE:
if (canputnext(RD(q))) {
sizeof (hidp->hid_report_descr));
/*
* can't allocate mblk, indicate
* that nothing is returned
*/
} else {
sizeof (hidp->hid_report_descr);
}
return (HID_SUCCESS);
} else {
/* retry */
return (HID_ENQUEUE);
}
case HID_GET_VID_PID:
if (canputnext(RD(q))) {
/*
* can't allocate mblk, indicate that nothing
* is being returned.
*/
} else {
sizeof (hid_vid_pid_t);
}
return (HID_SUCCESS);
} else {
/* retry */
return (HID_ENQUEUE);
}
case HID_OPEN_POLLED_INPUT:
if (canputnext(RD(q))) {
/* Initialize the structure */
(uchar_t *)&hid_polled_input,
sizeof (hid_polled_input_callback_t));
/*
* can't allocate mblk, indicate that nothing
* is being returned.
*/
} else {
/* Call down into USBA */
(void) hid_polled_input_init(hidp);
sizeof (hid_polled_input_callback_t);
}
return (HID_SUCCESS);
} else {
/* retry */
return (HID_ENQUEUE);
}
case HID_CLOSE_POLLED_INPUT:
/* Call down into USBA */
(void) hid_polled_input_fini(hidp);
return (HID_SUCCESS);
default:
return (HID_FAILURE);
}
/*
* These (device executable) commands require a hid_req_t.
* Make sure one is present
*/
return (error);
} else {
(hid_req_data->hid_req_wLength == 0)) {
return (error);
}
}
/*
* Check is version no. is correct. This
* is coming from the user
*/
return (error);
}
"hid_mctl_receive: dev_state=%s",
switch (hidp->hid_dev_state) {
case USB_DEV_PWRED_DOWN:
/*
* get the device full powered. We get a callback
* which enables the WQ and kicks off IO
*/
hidp, 0) != USB_SUCCESS) {
/* we retry raising power in wsrv */
}
error = HID_ENQUEUE;
break;
case USB_DEV_HID_POWER_CHANGE:
error = HID_ENQUEUE;
break;
case USB_DEV_ONLINE:
/* Send a message down */
hid_req_data, mp);
if (error == HID_FAILURE) {
}
} else {
}
break;
default:
break;
}
return (error);
}
/*
* hid_mctl_execute_cmd:
* Send the command to the device.
*/
static int
{
int request_index;
"hid_mctl_execute_cmd: iocp=0x%p", (void *)iocp);
/*
* Set up the argument to be passed back to hid
* when the asynchronous control callback is
* executed.
*/
if (def_pipe_arg == NULL) {
return (HID_FAILURE);
}
/*
* Send the command down to USBA through default
* pipe.
*/
return (HID_FAILURE);
}
return (HID_INPROGRESS);
}
/*
* hid_send_async_ctrl_request:
* Send an asynchronous control request to USBA. Since hid is a STREAMS
* driver, it is not allowed to wait in its entry points except for the
* open and close entry points. Therefore, hid must use the asynchronous
* USBA calls.
*/
static int
{
int rval;
"hid_send_async_ctrl_request: "
"rq_type=%d rq_rq=%d index=%d",
/*
* Note that ctrl_req->ctrl_data should be allocated by usba
* only for IN requests. OUT request(e.g SET_REPORT) can have a
* non-zero wLength value but ctrl_data would be allocated by
* client for them.
*/
"hid_req_wLength is exceeded");
return (USB_FAILURE);
}
}
"unable to alloc ctrl req. async trans failed");
return (USB_FAILURE);
}
}
/* host to device: create a msg from hid_req_data */
return (USB_FAILURE);
}
}
ctrl_req, 0)) != USB_SUCCESS) {
"usb_pipe_ctrl_xfer() failed. rval = %d", rval);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* hid_ioctl:
* Hid currently doesn't handle any ioctls. NACK
* the ioctl request.
*/
static void
{
}
/*
* hid_create_pm_components:
* Create the pm components required for power management.
* supports a remote wakeup.
* For other hid devices they are created unconditionally.
*/
static void
{
"hid_create_pm_components: Begin");
/* Allocate the state structure */
hidpm->hid_pm_capabilities = 0;
case KEYBOARD_PROTOCOL:
case MOUSE_PROTOCOL:
USB_SUCCESS)) {
"hid_create_pm_components: Remote Wakeup Enabled");
USB_SUCCESS) {
}
}
break;
default:
USB_SUCCESS)) {
hidpm->hid_wakeup_enabled = 0;
}
break;
}
"hid_create_pm_components: END");
}
/*
* hid_is_pm_enabled
* Check if the device is pm enabled. Always enable
* pm on the new SUN mouse
*/
static int
{
/* check for overrides first */
if (hid_pm_mouse ||
"hid-mouse-pm-enable") == 1)) {
return (USB_SUCCESS);
}
/*
* Always enable PM for 1.05 or greater SUN mouse
* hidp->hid_dev_descr won't be NULL.
*/
return (USB_SUCCESS);
}
} else {
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
/*
* hid_save_device_state
*/
static void
{
queue_t *q;
"hid_save_device_state");
/*
* Send MCTLs up indicating that the device
* will loose its state
*/
q = hidq->hidq_queue;
if (canputnext(q)) {
}
}
}
/* stop polling on the intr pipe */
}
}
/*
* hid_restore_device_state:
* Set original configuration of the device.
* Reopen intr pipe.
* Enable wrq - this starts new transactions on the control pipe.
*/
static void
{
int rval;
"hid_restore_device_state: %s",
/* First bring the device to full power */
/*
* We failed the checkpoint, there is no need to restore
* the device state
*/
return;
}
/* Check if we are talking to the same device */
/* change the device state from suspended to disconnected */
goto nodev;
}
/* if the device had remote wakeup earlier, enable it again */
if (hidpm->hid_wakeup_enabled) {
USB_REMOTE_WAKEUP_ENABLE)) != USB_SUCCESS) {
"usb_handle_remote_wakeup failed (%d)", rval);
}
}
/*
* restart polling on the interrupt pipe only if the device
* was previously operational (open)
*/
"hid_restore_device_state:"
"unable to restart intr pipe poll"
" rval = %d ", rval);
/*
* change the device state from
* suspended to disconnected
*/
goto nodev;
}
"device is being re-connected");
}
/* set the device state ONLINE */
/* inform upstream modules that the device is back */
if (canputnext(rdq)) {
}
}
/* enable write side q */
}
} else {
/* set the device state ONLINE */
}
return;
/*
* Notify applications about device removal. This only
* applies to an external (aka. physical) open. Not sure how to
* notify consconfig to close the internal minor node.
*/
}
}
}
}
/*
* hid_qreply_merror:
* Pass an error message up.
*/
static void
{
}
}
/*
* hid_data2mblk:
* Form an mblk from the given data
*/
static mblk_t *
{
if (len >= 0) {
if (mp) {
}
}
return (mp);
}
/*
* hid_flush :
* Flush data already sent upstreams to client module.
*/
static void
{
/*
* Flush pending data already sent upstream
*/
}
}
static void
{
"hid_pm_busy_component: %d",
"hid_pm_busy_component failed: %d",
}
}
}
static void
{
"hid_pm_idle_component: %d",
}
}
}
/*
* hid_pwrlvl0:
* Functions to handle power transition for various levels
* These functions act as place holders to issue USB commands
* to the devices to change their power levels
*/
static int
{
int rval;
queue_t *q;
switch (hidp->hid_dev_state) {
case USB_DEV_ONLINE:
/* Deny the powerdown request if the device is busy */
if (hidpm->hid_pm_busy != 0) {
return (USB_FAILURE);
}
if (canputnext(q)) {
/* try to preallocate mblks */
mp_fullpwr = allocb(
(mp_fullpwr != NULL)) {
/* stop polling */
/*
* Send an MCTL up indicating that
* we are powering off
*/
/* save the full powr mblk */
} else {
/*
* Since we failed to allocate one
* or more mblks, we fail attempt
* to go into low power this time
*/
return (USB_FAILURE);
}
} else {
/*
* Since we can't send an mblk up,
* we fail this attempt to go to low power
*/
return (USB_FAILURE);
}
}
/* Issue USB D3 command to the device here */
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
case USB_DEV_PWRED_DOWN:
default:
break;
}
return (USB_SUCCESS);
}
/* ARGSUSED */
static int
{
int rval;
/* Issue USB D2 command to the device here */
return (USB_FAILURE);
}
/* ARGSUSED */
static int
{
int rval;
return (USB_FAILURE);
}
static int
{
int rval;
queue_t *q;
switch (hidp->hid_dev_state) {
case USB_DEV_HID_POWER_CHANGE:
case USB_DEV_PWRED_DOWN:
/* Issue USB D0 command to the device here */
/* restart polling on intr pipe */
if (rval != USB_SUCCESS) {
"unable to restart intr polling rval = %d",
rval);
return (USB_FAILURE);
}
/* Send an MCTL up indicating device in full power */
if (canputnext(q)) {
} else {
}
}
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
case USB_DEV_ONLINE:
return (USB_SUCCESS);
default:
"hid_pwrlvl3: Improper State");
return (USB_FAILURE);
}
}
/*
* hid_polled_input_init :
* This routine calls down to the lower layers to initialize any state
* information. This routine initializes the lower layers for input.
*/
static int
{
"hid_polled_input_init");
/*
* Call the lower layers to intialize any state information
* that they will need to provide the polled characters.
*/
/*
* If for some reason the lower layers cannot initialized, then
* bail.
*/
(void) hid_polled_input_fini(hidp);
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* hid_polled_input_fini:
* This routine is called when we are done using this device as an input
* device.
*/
static int
{
"hid_polled_input_fini");
/*
* Call the lower layers to free any state information
* only if polled input has been initialised.
*/
if ((hidp->hid_polled_console_info) &&
USB_SUCCESS)) {
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* hid_polled_input_enter:
* This is the routine that is called in polled mode to save the USB
* state information before using the USB keyboard as an input device.
* This routine, and all of the routines that it calls, are responsible
* for saving any state information so that it can be restored when
* polling mode is over.
*/
static int
/* ARGSUSED */
{
/*
* Call the lower layers to tell them to save any state information.
*/
return (USB_SUCCESS);
}
/*
* hid_polled_read :
* This is the routine that is called in polled mode when it wants to read
* a character. We will call to the lower layers to see if there is any
* input data available. If there is USB scancodes available, we will
* give them back.
*/
static int
{
/*
* Call the lower layers to get the character from the controller.
* The lower layers will return the number of characters that
* were put in the raw buffer. The address of the raw buffer
* was passed down to the lower layers during hid_polled_init.
*/
&num_bytes) != USB_SUCCESS) {
return (0);
}
/*
* Return the number of characters that were copied into the
* polled buffer.
*/
return (num_bytes);
}
/*
* hid_polled_input_exit :
* This is the routine that is called in polled mode when it is giving up
* control of the USB keyboard. This routine, and the lower layer routines
* that it calls, are responsible for restoring the controller state to the
* state it was in before polled mode.
*/
static int
{
/*
* Call the lower layers to restore any state information.
*/
return (0);
}