xref: /onnv-gate/usr/src/uts/common/io/usb/clients/hid/hid.c (revision 12819:b9f8177eb4e2)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51244Scg149915  * Common Development and Distribution License (the "License").
61244Scg149915  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211244Scg149915 
220Sstevel@tonic-gate /*
2312419SFei.Feng@Sun.COM  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * Human Interface Device driver (HID)
290Sstevel@tonic-gate  *
300Sstevel@tonic-gate  * The HID driver is a software driver which acts as a class
310Sstevel@tonic-gate  * driver for USB human input devices like keyboard, mouse,
320Sstevel@tonic-gate  * joystick etc and provides the class-specific interfaces
330Sstevel@tonic-gate  * between these client driver modules and the Universal Serial
340Sstevel@tonic-gate  * Bus Driver(USBA).
350Sstevel@tonic-gate  *
360Sstevel@tonic-gate  * NOTE: This driver is not DDI compliant in that it uses undocumented
370Sstevel@tonic-gate  * functions for logging (USB_DPRINTF_L*, usb_alloc_log_hdl, usb_free_log_hdl).
380Sstevel@tonic-gate  *
390Sstevel@tonic-gate  * Undocumented functions may go away in a future Solaris OS release.
400Sstevel@tonic-gate  *
410Sstevel@tonic-gate  * Please see the DDK for sample code of these functions, and for the usbskel
420Sstevel@tonic-gate  * skeleton template driver which contains scaled-down versions of these
430Sstevel@tonic-gate  * functions written in a DDI-compliant way.
440Sstevel@tonic-gate  */
450Sstevel@tonic-gate 
460Sstevel@tonic-gate #define	USBDRV_MAJOR_VER	2
470Sstevel@tonic-gate #define	USBDRV_MINOR_VER	0
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include <sys/usb/usba.h>
500Sstevel@tonic-gate #include <sys/usb/usba/genconsole.h>
510Sstevel@tonic-gate #include <sys/usb/clients/hid/hid.h>
520Sstevel@tonic-gate #include <sys/usb/clients/hid/hid_polled.h>
530Sstevel@tonic-gate #include <sys/usb/clients/hidparser/hidparser.h>
540Sstevel@tonic-gate #include <sys/usb/clients/hid/hidvar.h>
550Sstevel@tonic-gate #include <sys/usb/clients/hid/hidminor.h>
560Sstevel@tonic-gate #include <sys/usb/clients/hidparser/hid_parser_driver.h>
570Sstevel@tonic-gate #include <sys/stropts.h>
581244Scg149915 #include <sys/sunddi.h>
5910153SAaron.Zang@Sun.COM #include <sys/stream.h>
6010153SAaron.Zang@Sun.COM #include <sys/strsun.h>
610Sstevel@tonic-gate 
620Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
631244Scg149915 
640Sstevel@tonic-gate /* Debugging support */
65880Sfrits uint_t	hid_errmask	= (uint_t)PRINT_MASK_ALL;
66880Sfrits uint_t	hid_errlevel	= USB_LOG_L4;
67880Sfrits uint_t	hid_instance_debug = (uint_t)-1;
680Sstevel@tonic-gate 
690Sstevel@tonic-gate /* tunables */
700Sstevel@tonic-gate int	hid_default_pipe_drain_timeout = HID_DEFAULT_PIPE_DRAIN_TIMEOUT;
71*12819SVincent.Wang@Sun.COM int	hid_pm_mouse = 1; /* enable remote_wakeup for USB mouse/keyboard */
720Sstevel@tonic-gate 
730Sstevel@tonic-gate /* soft state structures */
740Sstevel@tonic-gate #define	HID_INITIAL_SOFT_SPACE	4
750Sstevel@tonic-gate static void *hid_statep;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /* Callbacks */
780Sstevel@tonic-gate static void hid_interrupt_pipe_callback(usb_pipe_handle_t,
790Sstevel@tonic-gate 		usb_intr_req_t *);
800Sstevel@tonic-gate static void hid_default_pipe_callback(usb_pipe_handle_t, usb_ctrl_req_t *);
810Sstevel@tonic-gate static void hid_interrupt_pipe_exception_callback(usb_pipe_handle_t,
820Sstevel@tonic-gate 		usb_intr_req_t *);
830Sstevel@tonic-gate static void hid_default_pipe_exception_callback(usb_pipe_handle_t,
840Sstevel@tonic-gate 		usb_ctrl_req_t *);
850Sstevel@tonic-gate static int hid_restore_state_event_callback(dev_info_t *);
860Sstevel@tonic-gate static int hid_disconnect_event_callback(dev_info_t *);
870Sstevel@tonic-gate static int hid_cpr_suspend(hid_state_t *hidp);
880Sstevel@tonic-gate static void hid_cpr_resume(hid_state_t *hidp);
890Sstevel@tonic-gate static void hid_power_change_callback(void *arg, int rval);
900Sstevel@tonic-gate 
910Sstevel@tonic-gate /* Supporting routines */
920Sstevel@tonic-gate static size_t hid_parse_hid_descr(usb_hid_descr_t *, size_t,
930Sstevel@tonic-gate 		usb_alt_if_data_t *, usb_ep_data_t *);
940Sstevel@tonic-gate static int hid_parse_hid_descr_failure(hid_state_t *);
950Sstevel@tonic-gate static int hid_handle_report_descriptor(hid_state_t *, int);
960Sstevel@tonic-gate static void hid_set_idle(hid_state_t *);
970Sstevel@tonic-gate static void hid_set_protocol(hid_state_t *, int);
980Sstevel@tonic-gate static void hid_detach_cleanup(dev_info_t *, hid_state_t *);
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate static int hid_start_intr_polling(hid_state_t *);
1010Sstevel@tonic-gate static void hid_close_intr_pipe(hid_state_t *);
1029432SPengcheng.Chen@Sun.COM static int hid_mctl_execute_cmd(queue_t *, int, hid_req_t *,
1039432SPengcheng.Chen@Sun.COM 		mblk_t *);
1040Sstevel@tonic-gate static int hid_mctl_receive(queue_t *, mblk_t *);
1059432SPengcheng.Chen@Sun.COM static int hid_send_async_ctrl_request(hid_default_pipe_arg_t *, hid_req_t *,
1069432SPengcheng.Chen@Sun.COM 		uchar_t, int, ushort_t);
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate static void hid_create_pm_components(dev_info_t *, hid_state_t *);
1090Sstevel@tonic-gate static int hid_is_pm_enabled(dev_info_t *);
1100Sstevel@tonic-gate static void hid_restore_device_state(dev_info_t *, hid_state_t *);
1110Sstevel@tonic-gate static void hid_save_device_state(hid_state_t *);
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate static void hid_qreply_merror(queue_t *, mblk_t *, uchar_t);
1140Sstevel@tonic-gate static mblk_t *hid_data2mblk(uchar_t *, int);
1150Sstevel@tonic-gate static void hid_flush(queue_t *);
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate static int hid_pwrlvl0(hid_state_t *);
1180Sstevel@tonic-gate static int hid_pwrlvl1(hid_state_t *);
1190Sstevel@tonic-gate static int hid_pwrlvl2(hid_state_t *);
1200Sstevel@tonic-gate static int hid_pwrlvl3(hid_state_t *);
1210Sstevel@tonic-gate static void hid_pm_busy_component(hid_state_t *);
1220Sstevel@tonic-gate static void hid_pm_idle_component(hid_state_t *);
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate static int hid_polled_read(hid_polled_handle_t, uchar_t **);
1250Sstevel@tonic-gate static int hid_polled_input_enter(hid_polled_handle_t);
1260Sstevel@tonic-gate static int hid_polled_input_exit(hid_polled_handle_t);
1270Sstevel@tonic-gate static int hid_polled_input_init(hid_state_t *);
1280Sstevel@tonic-gate static int hid_polled_input_fini(hid_state_t *);
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate /* Streams entry points */
1310Sstevel@tonic-gate static int	hid_open(queue_t *, dev_t *, int, int, cred_t *);
1320Sstevel@tonic-gate static int	hid_close(queue_t *, int, cred_t *);
1330Sstevel@tonic-gate static int	hid_wput(queue_t *, mblk_t *);
1340Sstevel@tonic-gate static int	hid_wsrv(queue_t *);
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate /* dev_ops entry points */
1370Sstevel@tonic-gate static int	hid_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1380Sstevel@tonic-gate static int	hid_attach(dev_info_t *, ddi_attach_cmd_t);
1390Sstevel@tonic-gate static int	hid_detach(dev_info_t *, ddi_detach_cmd_t);
1400Sstevel@tonic-gate static int	hid_power(dev_info_t *, int, int);
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate /*
1430Sstevel@tonic-gate  * Warlock is not aware of the automatic locking mechanisms for
1440Sstevel@tonic-gate  * streams drivers.  The hid streams enter points are protected by
1450Sstevel@tonic-gate  * a per module perimeter.  If the locking in hid is a bottleneck
1460Sstevel@tonic-gate  * per queue pair or per queue locking may be used.  Since warlock
1470Sstevel@tonic-gate  * is not aware of the streams perimeters, these notes have been added.
1480Sstevel@tonic-gate  *
1490Sstevel@tonic-gate  * Note that the perimeters do not protect the driver from callbacks
1500Sstevel@tonic-gate  * happening while a streams entry point is executing.	So, the hid_mutex
1510Sstevel@tonic-gate  * has been created to protect the data.
1520Sstevel@tonic-gate  */
1530Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk))
1540Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", datab))
1550Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", msgb))
1560Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", queue))
1570Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_ctrl_req))
1580Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", usb_intr_req))
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate /* module information */
1610Sstevel@tonic-gate static struct module_info hid_mod_info = {
1620Sstevel@tonic-gate 	0x0ffff,			/* module id number */
1630Sstevel@tonic-gate 	"hid",				/* module name */
1640Sstevel@tonic-gate 	0,				/* min packet size accepted */
1650Sstevel@tonic-gate 	INFPSZ,				/* max packet size accepted */
1660Sstevel@tonic-gate 	512,				/* hi-water mark */
1670Sstevel@tonic-gate 	128				/* lo-water mark */
1680Sstevel@tonic-gate };
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate /* read queue information structure */
1710Sstevel@tonic-gate static struct qinit rinit = {
1720Sstevel@tonic-gate 	NULL,				/* put procedure not needed */
1730Sstevel@tonic-gate 	NULL,				/* service procedure not needed */
1740Sstevel@tonic-gate 	hid_open,			/* called on startup */
1750Sstevel@tonic-gate 	hid_close,			/* called on finish */
1760Sstevel@tonic-gate 	NULL,				/* for future use */
1770Sstevel@tonic-gate 	&hid_mod_info,			/* module information structure */
1780Sstevel@tonic-gate 	NULL				/* module statistics structure */
1790Sstevel@tonic-gate };
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate /* write queue information structure */
1820Sstevel@tonic-gate static struct qinit winit = {
1830Sstevel@tonic-gate 	hid_wput,			/* put procedure */
1840Sstevel@tonic-gate 	hid_wsrv,			/* service procedure */
1850Sstevel@tonic-gate 	NULL,				/* open not used on write side */
1860Sstevel@tonic-gate 	NULL,				/* close not used on write side */
1870Sstevel@tonic-gate 	NULL,				/* for future use */
1880Sstevel@tonic-gate 	&hid_mod_info,			/* module information structure */
1890Sstevel@tonic-gate 	NULL				/* module statistics structure */
1900Sstevel@tonic-gate };
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate struct streamtab hid_streamtab = {
1930Sstevel@tonic-gate 	&rinit,
1940Sstevel@tonic-gate 	&winit,
1950Sstevel@tonic-gate 	NULL,			/* not a MUX */
1960Sstevel@tonic-gate 	NULL			/* not a MUX */
1970Sstevel@tonic-gate };
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate struct cb_ops hid_cb_ops = {
2000Sstevel@tonic-gate 	nulldev,		/* open  */
2010Sstevel@tonic-gate 	nulldev,		/* close */
2020Sstevel@tonic-gate 	nulldev,		/* strategy */
2030Sstevel@tonic-gate 	nulldev,		/* print */
2040Sstevel@tonic-gate 	nulldev,		/* dump */
2050Sstevel@tonic-gate 	nulldev,		/* read */
2060Sstevel@tonic-gate 	nulldev,		/* write */
2070Sstevel@tonic-gate 	nulldev,		/* ioctl */
2080Sstevel@tonic-gate 	nulldev,		/* devmap */
2090Sstevel@tonic-gate 	nulldev,		/* mmap */
2100Sstevel@tonic-gate 	nulldev,		/* segmap */
2110Sstevel@tonic-gate 	nochpoll,		/* poll */
2120Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
2130Sstevel@tonic-gate 	&hid_streamtab,		/* streamtab  */
2140Sstevel@tonic-gate 	D_MP | D_MTPERQ
2150Sstevel@tonic-gate };
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate static struct dev_ops hid_ops = {
2190Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
2200Sstevel@tonic-gate 	0,			/* refcnt  */
2210Sstevel@tonic-gate 	hid_info,		/* info */
2220Sstevel@tonic-gate 	nulldev,		/* identify */
2230Sstevel@tonic-gate 	nulldev,		/* probe */
2240Sstevel@tonic-gate 	hid_attach,		/* attach */
2250Sstevel@tonic-gate 	hid_detach,		/* detach */
2260Sstevel@tonic-gate 	nodev,			/* reset */
2270Sstevel@tonic-gate 	&hid_cb_ops,		/* driver operations */
2280Sstevel@tonic-gate 	NULL,			/* bus operations */
2297656SSherry.Moore@Sun.COM 	hid_power,		/* power */
2307656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
2310Sstevel@tonic-gate };
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate static struct modldrv hidmodldrv =	{
2340Sstevel@tonic-gate 	&mod_driverops,
2357425SGongtian.Zhao@Sun.COM 	"USB HID Client Driver",
2360Sstevel@tonic-gate 	&hid_ops			/* driver ops */
2370Sstevel@tonic-gate };
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate static struct modlinkage modlinkage = {
2400Sstevel@tonic-gate 	MODREV_1,
2410Sstevel@tonic-gate 	&hidmodldrv,
2420Sstevel@tonic-gate 	NULL,
2430Sstevel@tonic-gate };
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate static usb_event_t hid_events = {
2460Sstevel@tonic-gate 	hid_disconnect_event_callback,
2470Sstevel@tonic-gate 	hid_restore_state_event_callback,
2480Sstevel@tonic-gate 	NULL,
2490Sstevel@tonic-gate 	NULL,
2500Sstevel@tonic-gate };
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate int
_init(void)2540Sstevel@tonic-gate _init(void)
2550Sstevel@tonic-gate {
2560Sstevel@tonic-gate 	int rval;
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	if (((rval = ddi_soft_state_init(&hid_statep, sizeof (hid_state_t),
2590Sstevel@tonic-gate 	    HID_INITIAL_SOFT_SPACE)) != 0)) {
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 		return (rval);
2620Sstevel@tonic-gate 	}
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 	if ((rval = mod_install(&modlinkage)) != 0) {
2650Sstevel@tonic-gate 		ddi_soft_state_fini(&hid_statep);
2660Sstevel@tonic-gate 	}
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	return (rval);
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate int
_fini(void)2730Sstevel@tonic-gate _fini(void)
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate 	int rval;
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	if ((rval = mod_remove(&modlinkage)) != 0) {
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 		return (rval);
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	ddi_soft_state_fini(&hid_statep);
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	return (rval);
2850Sstevel@tonic-gate }
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2890Sstevel@tonic-gate _info(struct modinfo *modinfop)
2900Sstevel@tonic-gate {
2910Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2920Sstevel@tonic-gate }
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate /*
2960Sstevel@tonic-gate  * hid_info :
2970Sstevel@tonic-gate  *	Get minor number, soft state structure etc.
2980Sstevel@tonic-gate  */
2990Sstevel@tonic-gate /*ARGSUSED*/
3000Sstevel@tonic-gate static int
hid_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3010Sstevel@tonic-gate hid_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
3020Sstevel@tonic-gate 			void *arg, void **result)
3030Sstevel@tonic-gate {
3040Sstevel@tonic-gate 	hid_state_t	*hidp = NULL;
3050Sstevel@tonic-gate 	int		error = DDI_FAILURE;
3060Sstevel@tonic-gate 	minor_t		minor = getminor((dev_t)arg);
3070Sstevel@tonic-gate 	int		instance = HID_MINOR_TO_INSTANCE(minor);
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	switch (infocmd) {
3100Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
3110Sstevel@tonic-gate 		if ((hidp = ddi_get_soft_state(hid_statep, instance)) != NULL) {
3120Sstevel@tonic-gate 			*result = hidp->hid_dip;
3130Sstevel@tonic-gate 			if (*result != NULL) {
3140Sstevel@tonic-gate 				error = DDI_SUCCESS;
3150Sstevel@tonic-gate 			}
3160Sstevel@tonic-gate 		} else
3170Sstevel@tonic-gate 			*result = NULL;
3180Sstevel@tonic-gate 		break;
3190Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
3200Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
3210Sstevel@tonic-gate 		error = DDI_SUCCESS;
3220Sstevel@tonic-gate 		break;
3230Sstevel@tonic-gate 	default:
3240Sstevel@tonic-gate 		break;
3250Sstevel@tonic-gate 	}
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	return (error);
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate /*
3320Sstevel@tonic-gate  * hid_attach :
3330Sstevel@tonic-gate  *	Gets called at the time of attach. Do allocation,
3340Sstevel@tonic-gate  *	and initialization of the software structure.
3350Sstevel@tonic-gate  *	Get all the descriptors, setup the
3360Sstevel@tonic-gate  *	report descriptor tree by calling hidparser
3370Sstevel@tonic-gate  *	function.
3380Sstevel@tonic-gate  */
3390Sstevel@tonic-gate static int
hid_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3400Sstevel@tonic-gate hid_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3410Sstevel@tonic-gate {
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	int			instance = ddi_get_instance(dip);
3440Sstevel@tonic-gate 	int			parse_hid_descr_error = 0;
3450Sstevel@tonic-gate 	hid_state_t		*hidp = NULL;
3460Sstevel@tonic-gate 	uint32_t		usage_page;
3470Sstevel@tonic-gate 	uint32_t		usage;
3480Sstevel@tonic-gate 	usb_client_dev_data_t	*dev_data;
3490Sstevel@tonic-gate 	usb_alt_if_data_t	*altif_data;
3500Sstevel@tonic-gate 	char			minor_name[HID_MINOR_NAME_LEN];
3510Sstevel@tonic-gate 	usb_ep_data_t		*ep_data;
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	switch (cmd) {
3540Sstevel@tonic-gate 		case DDI_ATTACH:
3550Sstevel@tonic-gate 			break;
3560Sstevel@tonic-gate 		case DDI_RESUME:
3570Sstevel@tonic-gate 			hidp = ddi_get_soft_state(hid_statep, instance);
3580Sstevel@tonic-gate 			hid_cpr_resume(hidp);
3590Sstevel@tonic-gate 			return (DDI_SUCCESS);
3600Sstevel@tonic-gate 		default:
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 			return (DDI_FAILURE);
3630Sstevel@tonic-gate 	}
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	/*
3660Sstevel@tonic-gate 	 * Allocate softstate information and get softstate pointer
3670Sstevel@tonic-gate 	 */
3680Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(hid_statep, instance) == DDI_SUCCESS) {
3690Sstevel@tonic-gate 		hidp = ddi_get_soft_state(hid_statep, instance);
3700Sstevel@tonic-gate 	}
3710Sstevel@tonic-gate 	if (hidp == NULL) {
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 		goto fail;
3740Sstevel@tonic-gate 	}
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	hidp->hid_log_handle = usb_alloc_log_hdl(dip, NULL, &hid_errlevel,
3776898Sfb209375 	    &hid_errmask, &hid_instance_debug, 0);
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	hidp->hid_instance = instance;
3800Sstevel@tonic-gate 	hidp->hid_dip = dip;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	/*
3830Sstevel@tonic-gate 	 * Register with USBA. Just retrieve interface descriptor
3840Sstevel@tonic-gate 	 */
3850Sstevel@tonic-gate 	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
3860Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
3870Sstevel@tonic-gate 		    "hid_attach: client attach failed");
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 		goto fail;
3900Sstevel@tonic-gate 	}
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
3930Sstevel@tonic-gate 	    USB_SUCCESS) {
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
3960Sstevel@tonic-gate 		    "hid_attach: usb_get_dev_data() failed");
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 		goto fail;
3990Sstevel@tonic-gate 	}
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	/* initialize mutex */
4020Sstevel@tonic-gate 	mutex_init(&hidp->hid_mutex, NULL, MUTEX_DRIVER,
4036898Sfb209375 	    dev_data->dev_iblock_cookie);
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	hidp->hid_attach_flags	|= HID_LOCK_INIT;
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	/* get interface data for alternate 0 */
4080Sstevel@tonic-gate 	altif_data = &dev_data->dev_curr_cfg->
4096898Sfb209375 	    cfg_if[dev_data->dev_curr_if].if_alt[0];
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
4120Sstevel@tonic-gate 	hidp->hid_dev_data	= dev_data;
4130Sstevel@tonic-gate 	hidp->hid_dev_descr	= dev_data->dev_descr;
4140Sstevel@tonic-gate 	hidp->hid_interfaceno	= dev_data->dev_curr_if;
4150Sstevel@tonic-gate 	hidp->hid_if_descr	= altif_data->altif_descr;
4161418Sqz150045 	/*
4171418Sqz150045 	 * Make sure that the bInterfaceProtocol only has meaning to
4181418Sqz150045 	 * Boot Interface Subclass.
4191418Sqz150045 	 */
4201418Sqz150045 	if (hidp->hid_if_descr.bInterfaceSubClass != BOOT_INTERFACE)
4211418Sqz150045 		hidp->hid_if_descr.bInterfaceProtocol = NONE_PROTOCOL;
4220Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	if ((ep_data = usb_lookup_ep_data(dip, dev_data,
4250Sstevel@tonic-gate 	    hidp->hid_interfaceno, 0, 0,
4260Sstevel@tonic-gate 	    (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) {
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
4290Sstevel@tonic-gate 		    "no interrupt IN endpoint found");
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 		goto fail;
4320Sstevel@tonic-gate 	}
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
4350Sstevel@tonic-gate 	hidp->hid_ep_intr_descr = ep_data->ep_descr;
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	/*
4380Sstevel@tonic-gate 	 * Attempt to find the hid descriptor, it could be after interface
4390Sstevel@tonic-gate 	 * or after endpoint descriptors
4400Sstevel@tonic-gate 	 */
4410Sstevel@tonic-gate 	if (hid_parse_hid_descr(&hidp->hid_hid_descr, USB_HID_DESCR_SIZE,
4420Sstevel@tonic-gate 	    altif_data, ep_data) != USB_HID_DESCR_SIZE) {
4430Sstevel@tonic-gate 		/*
4440Sstevel@tonic-gate 		 * If parsing of hid descriptor failed and
4450Sstevel@tonic-gate 		 * the device is a keyboard or mouse, use predefined
4460Sstevel@tonic-gate 		 * length and packet size.
4470Sstevel@tonic-gate 		 */
4480Sstevel@tonic-gate 		if (hid_parse_hid_descr_failure(hidp) == USB_FAILURE) {
4490Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 			goto fail;
4520Sstevel@tonic-gate 		}
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 		/*
4550Sstevel@tonic-gate 		 * hid descriptor was bad but since
4560Sstevel@tonic-gate 		 * the device is a keyboard or mouse,
4570Sstevel@tonic-gate 		 * we will use the default length
4580Sstevel@tonic-gate 		 * and packet size.
4590Sstevel@tonic-gate 		 */
4600Sstevel@tonic-gate 		parse_hid_descr_error = HID_BAD_DESCR;
4610Sstevel@tonic-gate 	} else {
4620Sstevel@tonic-gate 		/* Parse hid descriptor successful */
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ATTA, hidp->hid_log_handle,
4650Sstevel@tonic-gate 		    "Hid descriptor:\n\t"
4660Sstevel@tonic-gate 		    "bLength = 0x%x bDescriptorType = 0x%x "
4670Sstevel@tonic-gate 		    "bcdHID = 0x%x\n\t"
4680Sstevel@tonic-gate 		    "bCountryCode = 0x%x bNumDescriptors = 0x%x\n\t"
4690Sstevel@tonic-gate 		    "bReportDescriptorType = 0x%x\n\t"
4700Sstevel@tonic-gate 		    "wReportDescriptorLength = 0x%x",
4710Sstevel@tonic-gate 		    hidp->hid_hid_descr.bLength,
4720Sstevel@tonic-gate 		    hidp->hid_hid_descr.bDescriptorType,
4730Sstevel@tonic-gate 		    hidp->hid_hid_descr.bcdHID,
4740Sstevel@tonic-gate 		    hidp->hid_hid_descr.bCountryCode,
4750Sstevel@tonic-gate 		    hidp->hid_hid_descr.bNumDescriptors,
4760Sstevel@tonic-gate 		    hidp->hid_hid_descr.bReportDescriptorType,
4770Sstevel@tonic-gate 		    hidp->hid_hid_descr.wReportDescriptorLength);
4780Sstevel@tonic-gate 	}
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	/*
4810Sstevel@tonic-gate 	 * Save a copy of the default pipe for easy reference
4820Sstevel@tonic-gate 	 */
4830Sstevel@tonic-gate 	hidp->hid_default_pipe = hidp->hid_dev_data->dev_default_ph;
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	/* we copied the descriptors we need, free the dev_data */
4860Sstevel@tonic-gate 	usb_free_dev_data(dip, dev_data);
4870Sstevel@tonic-gate 	hidp->hid_dev_data = NULL;
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	/*
4900Sstevel@tonic-gate 	 * Don't get the report descriptor if parsing hid descriptor earlier
4910Sstevel@tonic-gate 	 * failed since device probably won't return valid report descriptor
4920Sstevel@tonic-gate 	 * either. Though parsing of hid descriptor failed, we have reached
4930Sstevel@tonic-gate 	 * this point because the device has been identified as a
4940Sstevel@tonic-gate 	 * keyboard or a mouse successfully and the default packet
4950Sstevel@tonic-gate 	 * size and layout(in case of keyboard only) will be used, so it
4960Sstevel@tonic-gate 	 * is ok to go ahead even if parsing of hid descriptor failed and
4970Sstevel@tonic-gate 	 * we will not try to get the report descriptor.
4980Sstevel@tonic-gate 	 */
4990Sstevel@tonic-gate 	if (parse_hid_descr_error != HID_BAD_DESCR) {
5000Sstevel@tonic-gate 		/*
5010Sstevel@tonic-gate 		 * Sun mouse rev 105 is a bit slow in responding to this
5020Sstevel@tonic-gate 		 * request and requires multiple retries
5030Sstevel@tonic-gate 		 */
5040Sstevel@tonic-gate 		int retry;
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 		/*
5070Sstevel@tonic-gate 		 * Get and parse the report descriptor.
5080Sstevel@tonic-gate 		 * Set the packet size if parsing is successful.
5090Sstevel@tonic-gate 		 * Note that we start retry at 1 to have a delay
5100Sstevel@tonic-gate 		 * in the first iteration.
5110Sstevel@tonic-gate 		 */
5120Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
5130Sstevel@tonic-gate 		for (retry = 1; retry < HID_RETRY; retry++) {
5140Sstevel@tonic-gate 			if (hid_handle_report_descriptor(hidp,
5150Sstevel@tonic-gate 			    hidp->hid_interfaceno) == USB_SUCCESS) {
5160Sstevel@tonic-gate 				break;
5170Sstevel@tonic-gate 			}
5180Sstevel@tonic-gate 			delay(retry * drv_usectohz(1000));
5190Sstevel@tonic-gate 		}
5200Sstevel@tonic-gate 		if (retry >= HID_RETRY) {
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 			goto fail;
5230Sstevel@tonic-gate 		}
5240Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 		/*
5270Sstevel@tonic-gate 		 * If packet size is zero, but the device is identified
5280Sstevel@tonic-gate 		 * as a mouse or a keyboard, use predefined packet
5290Sstevel@tonic-gate 		 * size.
5300Sstevel@tonic-gate 		 */
5310Sstevel@tonic-gate 		if (hidp->hid_packet_size == 0) {
5320Sstevel@tonic-gate 			if (hidp->hid_if_descr.bInterfaceProtocol ==
5330Sstevel@tonic-gate 			    KEYBOARD_PROTOCOL) {
5340Sstevel@tonic-gate 				/* device is a keyboard */
5350Sstevel@tonic-gate 				hidp->hid_packet_size = USBKPSZ;
5360Sstevel@tonic-gate 			} else if (hidp->
5370Sstevel@tonic-gate 			    hid_if_descr.bInterfaceProtocol ==
5380Sstevel@tonic-gate 			    MOUSE_PROTOCOL) {
5390Sstevel@tonic-gate 				/* device is a mouse */
5400Sstevel@tonic-gate 				hidp->hid_packet_size = USBMSSZ;
5410Sstevel@tonic-gate 			} else {
5420Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_ATTA,
5430Sstevel@tonic-gate 				    hidp->hid_log_handle,
5440Sstevel@tonic-gate 				    "Failed to find hid packet size");
5450Sstevel@tonic-gate 				mutex_exit(&hidp->hid_mutex);
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 				goto fail;
5480Sstevel@tonic-gate 			}
5490Sstevel@tonic-gate 		}
5500Sstevel@tonic-gate 	}
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 	/*
5530Sstevel@tonic-gate 	 * initialize the pipe policy for the interrupt pipe.
5540Sstevel@tonic-gate 	 */
5550Sstevel@tonic-gate 	hidp->hid_intr_pipe_policy.pp_max_async_reqs = 1;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	/*
5580Sstevel@tonic-gate 	 * Make a clas specific request to SET_IDLE
5590Sstevel@tonic-gate 	 * In this case send no reports if state has not changed.
5600Sstevel@tonic-gate 	 * See HID 7.2.4.
5610Sstevel@tonic-gate 	 */
5620Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
5630Sstevel@tonic-gate 	hid_set_idle(hidp);
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	/* always initialize to report protocol */
5660Sstevel@tonic-gate 	hid_set_protocol(hidp, SET_REPORT_PROTOCOL);
5670Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	/*
5700Sstevel@tonic-gate 	 * Create minor node based on information from the
5710Sstevel@tonic-gate 	 * descriptors
5720Sstevel@tonic-gate 	 */
5730Sstevel@tonic-gate 	switch (hidp->hid_if_descr.bInterfaceProtocol) {
5740Sstevel@tonic-gate 	case KEYBOARD_PROTOCOL:
5750Sstevel@tonic-gate 		(void) strcpy(minor_name, "keyboard");
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 		break;
5780Sstevel@tonic-gate 	case MOUSE_PROTOCOL:
5790Sstevel@tonic-gate 		(void) strcpy(minor_name, "mouse");
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 		break;
5820Sstevel@tonic-gate 	default:
5839237SStrony.Zhang@Sun.COM 		/*
5849237SStrony.Zhang@Sun.COM 		 * If the report descriptor has the GD mouse collection in
5859237SStrony.Zhang@Sun.COM 		 * its multiple collection, create a minor node and support it.
5869237SStrony.Zhang@Sun.COM 		 * It is used on some advanced keyboard/mouse set.
5879237SStrony.Zhang@Sun.COM 		 */
5889237SStrony.Zhang@Sun.COM 		if (hidparser_lookup_usage_collection(
5899237SStrony.Zhang@Sun.COM 		    hidp->hid_report_descr, HID_GENERIC_DESKTOP,
5909237SStrony.Zhang@Sun.COM 		    HID_GD_MOUSE) != HIDPARSER_FAILURE) {
5919237SStrony.Zhang@Sun.COM 			(void) strcpy(minor_name, "mouse");
5929237SStrony.Zhang@Sun.COM 
5939237SStrony.Zhang@Sun.COM 			break;
5949237SStrony.Zhang@Sun.COM 		}
5959237SStrony.Zhang@Sun.COM 
5960Sstevel@tonic-gate 		if (hidparser_get_top_level_collection_usage(
5970Sstevel@tonic-gate 		    hidp->hid_report_descr, &usage_page, &usage) !=
5980Sstevel@tonic-gate 		    HIDPARSER_FAILURE) {
5990Sstevel@tonic-gate 			switch (usage_page) {
6000Sstevel@tonic-gate 			case HID_CONSUMER:
6010Sstevel@tonic-gate 				switch (usage) {
6020Sstevel@tonic-gate 				case HID_CONSUMER_CONTROL:
6030Sstevel@tonic-gate 					(void) strcpy(minor_name,
6040Sstevel@tonic-gate 					    "consumer_control");
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 					break;
6070Sstevel@tonic-gate 				default:
6080Sstevel@tonic-gate 					(void) sprintf(minor_name,
6090Sstevel@tonic-gate 					    "hid_%d_%d", usage_page, usage);
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 					break;
6120Sstevel@tonic-gate 				}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 				break;
6150Sstevel@tonic-gate 			case HID_GENERIC_DESKTOP:
6160Sstevel@tonic-gate 				switch (usage) {
6170Sstevel@tonic-gate 				case HID_GD_POINTER:
6180Sstevel@tonic-gate 					(void) strcpy(minor_name,
6190Sstevel@tonic-gate 					    "pointer");
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 					break;
6220Sstevel@tonic-gate 				case HID_GD_MOUSE:
6230Sstevel@tonic-gate 					(void) strcpy(minor_name,
6240Sstevel@tonic-gate 					    "mouse");
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 					break;
6270Sstevel@tonic-gate 				case HID_GD_KEYBOARD:
6280Sstevel@tonic-gate 					(void) strcpy(minor_name,
6290Sstevel@tonic-gate 					    "keyboard");
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 					break;
6320Sstevel@tonic-gate 				default:
6330Sstevel@tonic-gate 					(void) sprintf(minor_name,
6340Sstevel@tonic-gate 					    "hid_%d_%d", usage_page, usage);
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 					break;
6370Sstevel@tonic-gate 				}
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 				break;
6400Sstevel@tonic-gate 			default:
6410Sstevel@tonic-gate 				(void) sprintf(minor_name,
6420Sstevel@tonic-gate 				    "hid_%d_%d", usage_page, usage);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 				break;
6450Sstevel@tonic-gate 			}
6460Sstevel@tonic-gate 		} else {
6470Sstevel@tonic-gate 			USB_DPRINTF_L1(PRINT_MASK_ATTA, hidp->hid_log_handle,
6480Sstevel@tonic-gate 			    "hid_attach: Unsupported HID device");
6490Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 			goto fail;
6520Sstevel@tonic-gate 		}
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 		break;
6550Sstevel@tonic-gate 	}
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	if ((ddi_create_minor_node(dip, minor_name, S_IFCHR,
6600Sstevel@tonic-gate 	    HID_CONSTRUCT_EXTERNAL_MINOR(instance),
6610Sstevel@tonic-gate 	    DDI_PSEUDO, 0)) != DDI_SUCCESS) {
662978Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
6630Sstevel@tonic-gate 		    "hid_attach: Could not create minor node");
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 		goto fail;
6660Sstevel@tonic-gate 	}
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	/* create internal path for virtual */
6690Sstevel@tonic-gate 	if (strcmp(minor_name, "mouse") == 0) {
6700Sstevel@tonic-gate 		if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
6710Sstevel@tonic-gate 		    HID_CONSTRUCT_INTERNAL_MINOR(instance)) != DDI_SUCCESS) {
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 			goto fail;
6740Sstevel@tonic-gate 		}
6750Sstevel@tonic-gate 	}
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	if (strcmp(minor_name, "keyboard") == 0) {
6780Sstevel@tonic-gate 		if (ddi_create_internal_pathname(dip, "internal_keyboard",
6790Sstevel@tonic-gate 		    S_IFCHR, HID_CONSTRUCT_INTERNAL_MINOR(instance)) !=
6800Sstevel@tonic-gate 		    DDI_SUCCESS) {
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 			goto fail;
6830Sstevel@tonic-gate 		}
6840Sstevel@tonic-gate 	}
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
6870Sstevel@tonic-gate 	hidp->hid_attach_flags |= HID_MINOR_NODES;
6880Sstevel@tonic-gate 	hidp->hid_dev_state = USB_DEV_ONLINE;
6890Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 	/* register for all events */
6920Sstevel@tonic-gate 	if (usb_register_event_cbs(dip, &hid_events, 0) != USB_SUCCESS) {
693978Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
6940Sstevel@tonic-gate 		    "usb_register_event_cbs failed");
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 		goto fail;
6970Sstevel@tonic-gate 	}
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 	/* now create components to power manage this device */
7000Sstevel@tonic-gate 	hid_create_pm_components(dip, hidp);
7010Sstevel@tonic-gate 	hid_pm_busy_component(hidp);
7020Sstevel@tonic-gate 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
7030Sstevel@tonic-gate 	hid_pm_idle_component(hidp);
7040Sstevel@tonic-gate 
70510153SAaron.Zang@Sun.COM 	hidp->hid_internal_rq = hidp->hid_external_rq = NULL;
70610153SAaron.Zang@Sun.COM 	hidp->hid_internal_flag = hidp->hid_external_flag = 0;
70710153SAaron.Zang@Sun.COM 	hidp->hid_inuse_rq = NULL;
70810153SAaron.Zang@Sun.COM 
7090Sstevel@tonic-gate 	/*
7100Sstevel@tonic-gate 	 * report device
7110Sstevel@tonic-gate 	 */
7120Sstevel@tonic-gate 	ddi_report_dev(dip);
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
7150Sstevel@tonic-gate 	    "hid_attach: End");
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	return (DDI_SUCCESS);
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate fail:
7200Sstevel@tonic-gate 	if (hidp) {
7210Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
7220Sstevel@tonic-gate 		    "hid_attach: fail");
7230Sstevel@tonic-gate 		hid_detach_cleanup(dip, hidp);
7240Sstevel@tonic-gate 	}
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	return (DDI_FAILURE);
7270Sstevel@tonic-gate }
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate /*
7310Sstevel@tonic-gate  * hid_detach :
7320Sstevel@tonic-gate  *	Gets called at the time of detach.
7330Sstevel@tonic-gate  */
7340Sstevel@tonic-gate static int
hid_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)7350Sstevel@tonic-gate hid_detach(dev_info_t *dip, ddi_detach_cmd_t	cmd)
7360Sstevel@tonic-gate {
7370Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
7380Sstevel@tonic-gate 	hid_state_t	*hidp;
7390Sstevel@tonic-gate 	int		rval = DDI_FAILURE;
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	hidp = ddi_get_soft_state(hid_statep, instance);
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle, "hid_detach");
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	switch (cmd) {
7460Sstevel@tonic-gate 	case DDI_DETACH:
7470Sstevel@tonic-gate 		/*
7480Sstevel@tonic-gate 		 * Undo	what we	did in client_attach, freeing resources
7490Sstevel@tonic-gate 		 * and removing	things we installed.  The system
7500Sstevel@tonic-gate 		 * framework guarantees	we are not active with this devinfo
7510Sstevel@tonic-gate 		 * node	in any other entry points at this time.
7520Sstevel@tonic-gate 		 */
7530Sstevel@tonic-gate 		hid_detach_cleanup(dip, hidp);
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 		return (DDI_SUCCESS);
7560Sstevel@tonic-gate 	case DDI_SUSPEND:
7570Sstevel@tonic-gate 		rval = hid_cpr_suspend(hidp);
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 		return (rval == USB_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
7600Sstevel@tonic-gate 	default:
7610Sstevel@tonic-gate 		break;
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	return (rval);
7650Sstevel@tonic-gate }
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate /*
7680Sstevel@tonic-gate  * hid_open :
7690Sstevel@tonic-gate  *	Open entry point: Opens the interrupt pipe.  Sets up queues.
7700Sstevel@tonic-gate  */
7710Sstevel@tonic-gate /*ARGSUSED*/
7720Sstevel@tonic-gate static int
hid_open(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp)7730Sstevel@tonic-gate hid_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
7740Sstevel@tonic-gate {
7750Sstevel@tonic-gate 	int no_of_ep = 0;
7760Sstevel@tonic-gate 	int rval;
7770Sstevel@tonic-gate 	int instance;
7780Sstevel@tonic-gate 	hid_state_t *hidp;
7790Sstevel@tonic-gate 	minor_t minor = getminor(*devp);
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	instance = HID_MINOR_TO_INSTANCE(minor);
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	hidp = ddi_get_soft_state(hid_statep, instance);
7840Sstevel@tonic-gate 	if (hidp == NULL) {
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 		return (ENXIO);
7870Sstevel@tonic-gate 	}
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_OPEN, hidp->hid_log_handle,
7900Sstevel@tonic-gate 	    "hid_open: Begin");
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	if (sflag) {
7930Sstevel@tonic-gate 		/* clone open NOT supported here */
7940Sstevel@tonic-gate 		return (ENXIO);
7950Sstevel@tonic-gate 	}
7960Sstevel@tonic-gate 
7979432SPengcheng.Chen@Sun.COM 	if (!(flag & FREAD)) {
7989432SPengcheng.Chen@Sun.COM 		return (EIO);
7999432SPengcheng.Chen@Sun.COM 	}
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	/*
8020Sstevel@tonic-gate 	 * This is a workaround:
8030Sstevel@tonic-gate 	 *	Currently, if we open an already disconnected device, and send
8040Sstevel@tonic-gate 	 *	a CONSOPENPOLL ioctl to it, the system will panic, please refer
8050Sstevel@tonic-gate 	 *	to the processing HID_OPEN_POLLED_INPUT ioctl in the routine
8060Sstevel@tonic-gate 	 *	hid_mctl_receive().
8070Sstevel@tonic-gate 	 *	The consconfig_dacf module need this interface to detect if the
8080Sstevel@tonic-gate 	 *	device is already disconnnected.
8090Sstevel@tonic-gate 	 */
8109432SPengcheng.Chen@Sun.COM 	mutex_enter(&hidp->hid_mutex);
8119666SPengcheng.Chen@Sun.COM 	if (HID_IS_INTERNAL_OPEN(minor) &&
8129666SPengcheng.Chen@Sun.COM 	    (hidp->hid_dev_state == USB_DEV_DISCONNECTED)) {
8130Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
8140Sstevel@tonic-gate 		return (ENODEV);
8150Sstevel@tonic-gate 	}
8160Sstevel@tonic-gate 
81710153SAaron.Zang@Sun.COM 	if (HID_IS_INTERNAL_OPEN(minor) &&
81810153SAaron.Zang@Sun.COM 	    (hidp->hid_internal_rq != NULL)) {
81910153SAaron.Zang@Sun.COM 		ASSERT(hidp->hid_internal_rq == q);
82010153SAaron.Zang@Sun.COM 
82110153SAaron.Zang@Sun.COM 		mutex_exit(&hidp->hid_mutex);
82210153SAaron.Zang@Sun.COM 		return (0);
8239432SPengcheng.Chen@Sun.COM 	}
82410153SAaron.Zang@Sun.COM 
82510153SAaron.Zang@Sun.COM 	if ((!HID_IS_INTERNAL_OPEN(minor)) &&
82610153SAaron.Zang@Sun.COM 	    (hidp->hid_external_rq != NULL)) {
82710153SAaron.Zang@Sun.COM 		ASSERT(hidp->hid_external_rq == q);
82810153SAaron.Zang@Sun.COM 
82910153SAaron.Zang@Sun.COM 		mutex_exit(&hidp->hid_mutex);
83010153SAaron.Zang@Sun.COM 		return (0);
83110153SAaron.Zang@Sun.COM 	}
83210153SAaron.Zang@Sun.COM 
8339432SPengcheng.Chen@Sun.COM 	mutex_exit(&hidp->hid_mutex);
8349432SPengcheng.Chen@Sun.COM 
83510153SAaron.Zang@Sun.COM 	q->q_ptr = hidp;
83610153SAaron.Zang@Sun.COM 	WR(q)->q_ptr = hidp;
8379432SPengcheng.Chen@Sun.COM 
8389432SPengcheng.Chen@Sun.COM 	mutex_enter(&hidp->hid_mutex);
83910153SAaron.Zang@Sun.COM 	if (hidp->hid_inuse_rq != NULL) {
84010153SAaron.Zang@Sun.COM 		/* Pipe has already been setup */
84110153SAaron.Zang@Sun.COM 
84210153SAaron.Zang@Sun.COM 		if (HID_IS_INTERNAL_OPEN(minor)) {
84310153SAaron.Zang@Sun.COM 			hidp->hid_internal_flag = HID_STREAMS_OPEN;
84410153SAaron.Zang@Sun.COM 			hidp->hid_inuse_rq = hidp->hid_internal_rq = q;
84510153SAaron.Zang@Sun.COM 		} else {
84610153SAaron.Zang@Sun.COM 			hidp->hid_external_flag = HID_STREAMS_OPEN;
84710153SAaron.Zang@Sun.COM 			hidp->hid_inuse_rq = hidp->hid_external_rq = q;
84810153SAaron.Zang@Sun.COM 		}
84910153SAaron.Zang@Sun.COM 
8500Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
85110153SAaron.Zang@Sun.COM 
8529432SPengcheng.Chen@Sun.COM 		qprocson(q);
85310153SAaron.Zang@Sun.COM 
8549432SPengcheng.Chen@Sun.COM 		return (0);
8550Sstevel@tonic-gate 	}
8560Sstevel@tonic-gate 
85710153SAaron.Zang@Sun.COM 	/* Pipe only needs to be opened once */
8589432SPengcheng.Chen@Sun.COM 	hidp->hid_interrupt_pipe = NULL;
8599432SPengcheng.Chen@Sun.COM 	no_of_ep = hidp->hid_if_descr.bNumEndpoints;
8609432SPengcheng.Chen@Sun.COM 	mutex_exit(&hidp->hid_mutex);
8619432SPengcheng.Chen@Sun.COM 
8629432SPengcheng.Chen@Sun.COM 	/* Check if interrupt endpoint exists */
8639432SPengcheng.Chen@Sun.COM 	if (no_of_ep > 0) {
8649432SPengcheng.Chen@Sun.COM 		/* Open the interrupt pipe */
8659432SPengcheng.Chen@Sun.COM 		if (usb_pipe_open(hidp->hid_dip,
8669432SPengcheng.Chen@Sun.COM 		    &hidp->hid_ep_intr_descr,
8679432SPengcheng.Chen@Sun.COM 		    &hidp->hid_intr_pipe_policy, USB_FLAGS_SLEEP,
8689432SPengcheng.Chen@Sun.COM 		    &hidp->hid_interrupt_pipe) !=
8699432SPengcheng.Chen@Sun.COM 		    USB_SUCCESS) {
8709432SPengcheng.Chen@Sun.COM 
8719432SPengcheng.Chen@Sun.COM 			q->q_ptr = NULL;
8729432SPengcheng.Chen@Sun.COM 			WR(q)->q_ptr = NULL;
8739432SPengcheng.Chen@Sun.COM 			return (EIO);
8740Sstevel@tonic-gate 		}
8750Sstevel@tonic-gate 	}
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	hid_pm_busy_component(hidp);
8780Sstevel@tonic-gate 	(void) pm_raise_power(hidp->hid_dip, 0, USB_DEV_OS_FULL_PWR);
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
88110153SAaron.Zang@Sun.COM 	if (HID_IS_INTERNAL_OPEN(minor)) {
88210153SAaron.Zang@Sun.COM 		hidp->hid_internal_flag = HID_STREAMS_OPEN;
88310153SAaron.Zang@Sun.COM 		hidp->hid_inuse_rq = hidp->hid_internal_rq = q;
88410153SAaron.Zang@Sun.COM 	} else {
88510153SAaron.Zang@Sun.COM 		hidp->hid_external_flag = HID_STREAMS_OPEN;
88610153SAaron.Zang@Sun.COM 		hidp->hid_inuse_rq = hidp->hid_external_rq = q;
88710153SAaron.Zang@Sun.COM 	}
88810153SAaron.Zang@Sun.COM 
8890Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 	qprocson(q);
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	if ((rval = hid_start_intr_polling(hidp)) != USB_SUCCESS) {
8960Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_OPEN, hidp->hid_log_handle,
8970Sstevel@tonic-gate 		    "unable to start intr pipe polling. rval = %d", rval);
8980Sstevel@tonic-gate 
89910153SAaron.Zang@Sun.COM 		if (HID_IS_INTERNAL_OPEN(minor))
90010153SAaron.Zang@Sun.COM 			hidp->hid_internal_flag = HID_STREAMS_DISMANTLING;
90110153SAaron.Zang@Sun.COM 		else
90210153SAaron.Zang@Sun.COM 			hidp->hid_external_flag = HID_STREAMS_DISMANTLING;
9030Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 		usb_pipe_close(hidp->hid_dip, hidp->hid_interrupt_pipe,
9060Sstevel@tonic-gate 		    USB_FLAGS_SLEEP, NULL, NULL);
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
9090Sstevel@tonic-gate 		hidp->hid_interrupt_pipe = NULL;
9100Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 		qprocsoff(q);
91310153SAaron.Zang@Sun.COM 
91410153SAaron.Zang@Sun.COM 		mutex_enter(&hidp->hid_mutex);
91510153SAaron.Zang@Sun.COM 		if (HID_IS_INTERNAL_OPEN(minor)) {
91610153SAaron.Zang@Sun.COM 			hidp->hid_internal_flag = 0;
91710153SAaron.Zang@Sun.COM 			hidp->hid_internal_rq = NULL;
91810153SAaron.Zang@Sun.COM 			if (hidp->hid_external_flag == HID_STREAMS_OPEN)
91910153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = hidp->hid_external_rq;
92010153SAaron.Zang@Sun.COM 			else
92110153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = NULL;
92210153SAaron.Zang@Sun.COM 		} else {
92310153SAaron.Zang@Sun.COM 			hidp->hid_external_flag = 0;
92410153SAaron.Zang@Sun.COM 			hidp->hid_external_rq = NULL;
92510153SAaron.Zang@Sun.COM 			if (hidp->hid_internal_flag == HID_STREAMS_OPEN)
92610153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = hidp->hid_internal_rq;
92710153SAaron.Zang@Sun.COM 			else
92810153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = NULL;
92910153SAaron.Zang@Sun.COM 		}
93010153SAaron.Zang@Sun.COM 		mutex_exit(&hidp->hid_mutex);
93110153SAaron.Zang@Sun.COM 
9329432SPengcheng.Chen@Sun.COM 		q->q_ptr = NULL;
9339432SPengcheng.Chen@Sun.COM 		WR(q)->q_ptr = NULL;
9349432SPengcheng.Chen@Sun.COM 
9350Sstevel@tonic-gate 		hid_pm_idle_component(hidp);
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 		return (EIO);
9380Sstevel@tonic-gate 	}
9390Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_OPEN, hidp->hid_log_handle, "hid_open: End");
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 	/*
9440Sstevel@tonic-gate 	 * Keyboard and mouse is Power managed by device activity.
9450Sstevel@tonic-gate 	 * All other devices go busy on open and idle on close.
9460Sstevel@tonic-gate 	 */
9470Sstevel@tonic-gate 	switch (hidp->hid_pm->hid_pm_strategy) {
9480Sstevel@tonic-gate 	case HID_PM_ACTIVITY:
9490Sstevel@tonic-gate 		hid_pm_idle_component(hidp);
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 		break;
9520Sstevel@tonic-gate 	default:
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 		break;
9550Sstevel@tonic-gate 	}
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	return (0);
9580Sstevel@tonic-gate }
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate /*
9620Sstevel@tonic-gate  * hid_close :
9630Sstevel@tonic-gate  *	Close entry point.
9640Sstevel@tonic-gate  */
9650Sstevel@tonic-gate /*ARGSUSED*/
9660Sstevel@tonic-gate static int
hid_close(queue_t * q,int flag,cred_t * credp)9670Sstevel@tonic-gate hid_close(queue_t *q, int flag, cred_t *credp)
9680Sstevel@tonic-gate {
96910153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)q->q_ptr;
9700Sstevel@tonic-gate 	queue_t		*wq;
9710Sstevel@tonic-gate 	mblk_t		*mp;
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle, "hid_close:");
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
97610153SAaron.Zang@Sun.COM 
97710153SAaron.Zang@Sun.COM 	ASSERT((hidp->hid_internal_rq == q) ||
97810153SAaron.Zang@Sun.COM 	    (hidp->hid_external_rq == q));
97910153SAaron.Zang@Sun.COM 
98010153SAaron.Zang@Sun.COM 	if (hidp->hid_internal_rq == q)
98110153SAaron.Zang@Sun.COM 		hidp->hid_internal_flag = HID_STREAMS_DISMANTLING;
98210153SAaron.Zang@Sun.COM 	else
98310153SAaron.Zang@Sun.COM 		hidp->hid_external_flag = HID_STREAMS_DISMANTLING;
98410153SAaron.Zang@Sun.COM 
9850Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate 	/*
9880Sstevel@tonic-gate 	 * In case there are any outstanding requests on
9890Sstevel@tonic-gate 	 * the default pipe, wait forever for them to complete.
9900Sstevel@tonic-gate 	 */
9910Sstevel@tonic-gate 	(void) usb_pipe_drain_reqs(hidp->hid_dip,
9920Sstevel@tonic-gate 	    hidp->hid_default_pipe, 0, USB_FLAGS_SLEEP, NULL, 0);
9930Sstevel@tonic-gate 
9949432SPengcheng.Chen@Sun.COM 	mutex_enter(&hidp->hid_mutex);
9959432SPengcheng.Chen@Sun.COM 	wq = WR(q);
9960Sstevel@tonic-gate 	/* drain any M_CTLS on the WQ */
9970Sstevel@tonic-gate 	while (mp = getq(wq)) {
9980Sstevel@tonic-gate 		hid_qreply_merror(wq, mp, EIO);
9990Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
10000Sstevel@tonic-gate 		hid_pm_idle_component(hidp);
10010Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
10020Sstevel@tonic-gate 	}
10030Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	qprocsoff(q);
10069432SPengcheng.Chen@Sun.COM 
10070Sstevel@tonic-gate 	q->q_ptr = NULL;
10089432SPengcheng.Chen@Sun.COM 	wq->q_ptr = NULL;
10099432SPengcheng.Chen@Sun.COM 
101010153SAaron.Zang@Sun.COM 	mutex_enter(&hidp->hid_mutex);
101110153SAaron.Zang@Sun.COM 
101210153SAaron.Zang@Sun.COM 	if (hidp->hid_internal_rq == q) {
101310153SAaron.Zang@Sun.COM 		hidp->hid_internal_rq = NULL;
101410153SAaron.Zang@Sun.COM 		hidp->hid_internal_flag = 0;
101510153SAaron.Zang@Sun.COM 		if (hidp->hid_inuse_rq == q) {
101610153SAaron.Zang@Sun.COM 			/* We are closing the active stream */
101710153SAaron.Zang@Sun.COM 			if (hidp->hid_external_flag == HID_STREAMS_OPEN)
101810153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = hidp->hid_external_rq;
101910153SAaron.Zang@Sun.COM 			else
102010153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = NULL;
102110153SAaron.Zang@Sun.COM 		}
10229432SPengcheng.Chen@Sun.COM 	} else {
102310153SAaron.Zang@Sun.COM 		hidp->hid_external_rq = NULL;
102410153SAaron.Zang@Sun.COM 		hidp->hid_external_flag = 0;
102510153SAaron.Zang@Sun.COM 		if (hidp->hid_inuse_rq == q) {
102610153SAaron.Zang@Sun.COM 			/* We are closing the active stream */
102710153SAaron.Zang@Sun.COM 			if (hidp->hid_internal_flag == HID_STREAMS_OPEN)
102810153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = hidp->hid_internal_rq;
102910153SAaron.Zang@Sun.COM 			else
103010153SAaron.Zang@Sun.COM 				hidp->hid_inuse_rq = NULL;
103110153SAaron.Zang@Sun.COM 		}
10329432SPengcheng.Chen@Sun.COM 	}
103310153SAaron.Zang@Sun.COM 
103410153SAaron.Zang@Sun.COM 	if (hidp->hid_inuse_rq != NULL) {
10359432SPengcheng.Chen@Sun.COM 		mutex_exit(&hidp->hid_mutex);
10369432SPengcheng.Chen@Sun.COM 		return (0);
10379432SPengcheng.Chen@Sun.COM 	}
10389432SPengcheng.Chen@Sun.COM 
10399432SPengcheng.Chen@Sun.COM 	/* all queues are closed, close USB pipes */
10409432SPengcheng.Chen@Sun.COM 	hid_close_intr_pipe(hidp);
10419432SPengcheng.Chen@Sun.COM 	mutex_exit(&hidp->hid_mutex);
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	/*
10440Sstevel@tonic-gate 	 * Devices other than keyboard/mouse go idle on close.
10450Sstevel@tonic-gate 	 */
10460Sstevel@tonic-gate 	switch (hidp->hid_pm->hid_pm_strategy) {
10470Sstevel@tonic-gate 	case HID_PM_ACTIVITY:
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 		break;
10500Sstevel@tonic-gate 	default:
10510Sstevel@tonic-gate 		hid_pm_idle_component(hidp);
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 		break;
10540Sstevel@tonic-gate 	}
10550Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle,
10560Sstevel@tonic-gate 	    "hid_close: End");
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	return (0);
10590Sstevel@tonic-gate }
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate /*
10630Sstevel@tonic-gate  * hid_wput :
10640Sstevel@tonic-gate  *	write put routine for the hid module
10650Sstevel@tonic-gate  */
10660Sstevel@tonic-gate static int
hid_wput(queue_t * q,mblk_t * mp)10670Sstevel@tonic-gate hid_wput(queue_t *q, mblk_t *mp)
10680Sstevel@tonic-gate {
106910153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)q->q_ptr;
10700Sstevel@tonic-gate 	int		error = USB_SUCCESS;
107110153SAaron.Zang@Sun.COM 	struct iocblk 	*iocbp;
107210153SAaron.Zang@Sun.COM 	mblk_t		*datap;
107310153SAaron.Zang@Sun.COM 	int		direction;
107410153SAaron.Zang@Sun.COM 	struct copyresp *crp;
107510153SAaron.Zang@Sun.COM 	queue_t 	*tmpq;
107610153SAaron.Zang@Sun.COM 	int		flag;
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
10790Sstevel@tonic-gate 	    "hid_wput: Begin");
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate 	/* See if the upper module is passing the right thing */
10820Sstevel@tonic-gate 	ASSERT(mp != NULL);
10830Sstevel@tonic-gate 	ASSERT(mp->b_datap != NULL);
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
10860Sstevel@tonic-gate 	case M_FLUSH:  /* Canonical flush handling */
10870Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
10880Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
10890Sstevel@tonic-gate 		}
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 		/* read queue not used so just send up */
10920Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
10930Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;
10940Sstevel@tonic-gate 			qreply(q, mp);
10950Sstevel@tonic-gate 		} else {
10960Sstevel@tonic-gate 			freemsg(mp);
10970Sstevel@tonic-gate 		}
10980Sstevel@tonic-gate 
10990Sstevel@tonic-gate 		break;
11000Sstevel@tonic-gate 	case M_IOCTL:
110110153SAaron.Zang@Sun.COM 		iocbp = (struct iocblk *)mp->b_rptr;
110210153SAaron.Zang@Sun.COM 
110310153SAaron.Zang@Sun.COM 		/* Only accept transparent ioctls */
110410153SAaron.Zang@Sun.COM 		if (iocbp->ioc_count != TRANSPARENT) {
110510153SAaron.Zang@Sun.COM 			miocnak(q, mp, 0, EINVAL);
110610153SAaron.Zang@Sun.COM 			break;
110710153SAaron.Zang@Sun.COM 		}
110810153SAaron.Zang@Sun.COM 
110910153SAaron.Zang@Sun.COM 		switch (iocbp->ioc_cmd) {
111010153SAaron.Zang@Sun.COM 		case HIDIOCKMGDIRECT:
111110153SAaron.Zang@Sun.COM 
111210153SAaron.Zang@Sun.COM 			mutex_enter(&hidp->hid_mutex);
111310153SAaron.Zang@Sun.COM 			ASSERT(hidp->hid_inuse_rq != NULL);
111410153SAaron.Zang@Sun.COM 			mutex_exit(&hidp->hid_mutex);
111510153SAaron.Zang@Sun.COM 
111610153SAaron.Zang@Sun.COM 			if ((datap = allocb(sizeof (int), BPRI_MED)) == NULL) {
111710153SAaron.Zang@Sun.COM 				miocnak(q, mp, 0, ENOMEM);
111810153SAaron.Zang@Sun.COM 				break;
111910153SAaron.Zang@Sun.COM 			}
112010153SAaron.Zang@Sun.COM 
112110153SAaron.Zang@Sun.COM 			mutex_enter(&hidp->hid_mutex);
112210153SAaron.Zang@Sun.COM 			if (hidp->hid_inuse_rq == hidp->hid_internal_rq) {
112310153SAaron.Zang@Sun.COM 				*(int *)datap->b_wptr = 0;
112410153SAaron.Zang@Sun.COM 				datap->b_wptr += sizeof (int);
112510153SAaron.Zang@Sun.COM 			} else {
112610153SAaron.Zang@Sun.COM 				ASSERT(hidp->hid_inuse_rq ==
112710153SAaron.Zang@Sun.COM 				    hidp->hid_external_rq);
112810153SAaron.Zang@Sun.COM 				*(int *)datap->b_wptr = 1;
112910153SAaron.Zang@Sun.COM 				datap->b_wptr += sizeof (int);
113010153SAaron.Zang@Sun.COM 			}
113110153SAaron.Zang@Sun.COM 			mutex_exit(&hidp->hid_mutex);
113210153SAaron.Zang@Sun.COM 
113310153SAaron.Zang@Sun.COM 			mcopyout(mp, NULL, sizeof (int), NULL, datap);
113410153SAaron.Zang@Sun.COM 			qreply(q, mp);
113510153SAaron.Zang@Sun.COM 			break;
113610153SAaron.Zang@Sun.COM 
113710153SAaron.Zang@Sun.COM 		case HIDIOCKMSDIRECT:
113810153SAaron.Zang@Sun.COM 			mcopyin(mp, NULL, sizeof (int), NULL);
113910153SAaron.Zang@Sun.COM 			qreply(q, mp);
114010153SAaron.Zang@Sun.COM 			break;
114110153SAaron.Zang@Sun.COM 
114210153SAaron.Zang@Sun.COM 		default:
114310153SAaron.Zang@Sun.COM 			miocnak(q, mp, 0, ENOTTY);
114410153SAaron.Zang@Sun.COM 		}
11450Sstevel@tonic-gate 
11460Sstevel@tonic-gate 		break;
114710153SAaron.Zang@Sun.COM 
114810153SAaron.Zang@Sun.COM 	case M_IOCDATA:
114910153SAaron.Zang@Sun.COM 
115010153SAaron.Zang@Sun.COM 		crp = (void *)mp->b_rptr;
115110153SAaron.Zang@Sun.COM 
115210153SAaron.Zang@Sun.COM 		if (crp->cp_rval != 0) {
115310153SAaron.Zang@Sun.COM 			miocnak(q, mp, 0, EIO);
115410153SAaron.Zang@Sun.COM 			break;
115510153SAaron.Zang@Sun.COM 		}
115610153SAaron.Zang@Sun.COM 
115710153SAaron.Zang@Sun.COM 		switch (crp->cp_cmd) {
115810153SAaron.Zang@Sun.COM 		case HIDIOCKMGDIRECT:
115910153SAaron.Zang@Sun.COM 			miocack(q, mp, 0, 0);
116010153SAaron.Zang@Sun.COM 			break;
116110153SAaron.Zang@Sun.COM 
116210153SAaron.Zang@Sun.COM 		case HIDIOCKMSDIRECT:
116310153SAaron.Zang@Sun.COM 			direction = *(int *)mp->b_cont->b_rptr;
116410153SAaron.Zang@Sun.COM 
116510153SAaron.Zang@Sun.COM 			if ((direction != 0) && (direction != 1)) {
116610153SAaron.Zang@Sun.COM 				miocnak(q, mp, 0, EINVAL);
116710153SAaron.Zang@Sun.COM 				break;
116810153SAaron.Zang@Sun.COM 			}
116910153SAaron.Zang@Sun.COM 
117010153SAaron.Zang@Sun.COM 			mutex_enter(&hidp->hid_mutex);
117110153SAaron.Zang@Sun.COM 
117210153SAaron.Zang@Sun.COM 			if (direction == 0) {
117310153SAaron.Zang@Sun.COM 				/* The internal stream is made active */
117410153SAaron.Zang@Sun.COM 				flag = hidp->hid_internal_flag;
117510153SAaron.Zang@Sun.COM 				tmpq = hidp->hid_internal_rq;
117610153SAaron.Zang@Sun.COM 			} else {
117710153SAaron.Zang@Sun.COM 				/* The external stream is made active */
117810153SAaron.Zang@Sun.COM 				flag = hidp->hid_external_flag;
117910153SAaron.Zang@Sun.COM 				tmpq = hidp->hid_external_rq;
118010153SAaron.Zang@Sun.COM 			}
118110153SAaron.Zang@Sun.COM 
118210153SAaron.Zang@Sun.COM 			if (flag != HID_STREAMS_OPEN) {
118310153SAaron.Zang@Sun.COM 				mutex_exit(&hidp->hid_mutex);
118410153SAaron.Zang@Sun.COM 				miocnak(q, mp, 0, EIO);
118510153SAaron.Zang@Sun.COM 				break;
118610153SAaron.Zang@Sun.COM 			}
118710153SAaron.Zang@Sun.COM 
118810153SAaron.Zang@Sun.COM 			hidp->hid_inuse_rq = tmpq;
118910153SAaron.Zang@Sun.COM 
119010153SAaron.Zang@Sun.COM 			mutex_exit(&hidp->hid_mutex);
119110153SAaron.Zang@Sun.COM 			miocack(q, mp, 0, 0);
119210153SAaron.Zang@Sun.COM 			break;
119310153SAaron.Zang@Sun.COM 
119410153SAaron.Zang@Sun.COM 		default:
119510153SAaron.Zang@Sun.COM 			miocnak(q, mp, 0, ENOTTY);
119610153SAaron.Zang@Sun.COM 			break;
119710153SAaron.Zang@Sun.COM 		}
119810153SAaron.Zang@Sun.COM 
119910153SAaron.Zang@Sun.COM 		break;
120010153SAaron.Zang@Sun.COM 
12010Sstevel@tonic-gate 	case M_CTL:
12020Sstevel@tonic-gate 		/* we are busy now */
12030Sstevel@tonic-gate 		hid_pm_busy_component(hidp);
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 		if (q->q_first) {
12060Sstevel@tonic-gate 			(void) putq(q, mp);
12070Sstevel@tonic-gate 		} else {
12080Sstevel@tonic-gate 			error = hid_mctl_receive(q, mp);
12090Sstevel@tonic-gate 			switch (error) {
12100Sstevel@tonic-gate 			case HID_ENQUEUE:
12110Sstevel@tonic-gate 				/*
12120Sstevel@tonic-gate 				 * put this mblk on the WQ for the wsrv to
12130Sstevel@tonic-gate 				 * process
12140Sstevel@tonic-gate 				 */
12150Sstevel@tonic-gate 				(void) putq(q, mp);
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 				break;
12180Sstevel@tonic-gate 			case HID_INPROGRESS:
12190Sstevel@tonic-gate 				/* request has been queued to the device */
12200Sstevel@tonic-gate 
12210Sstevel@tonic-gate 				break;
12220Sstevel@tonic-gate 			case HID_SUCCESS:
12230Sstevel@tonic-gate 				/*
12240Sstevel@tonic-gate 				 * returned by M_CTLS that are processed
12250Sstevel@tonic-gate 				 * immediately
12260Sstevel@tonic-gate 				 */
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate 				/* FALLTHRU */
12290Sstevel@tonic-gate 			case HID_FAILURE:
12300Sstevel@tonic-gate 			default:
12310Sstevel@tonic-gate 				hid_pm_idle_component(hidp);
12320Sstevel@tonic-gate 				break;
12330Sstevel@tonic-gate 			}
12340Sstevel@tonic-gate 		}
12350Sstevel@tonic-gate 		break;
12360Sstevel@tonic-gate 	default:
12370Sstevel@tonic-gate 		hid_qreply_merror(q, mp, EINVAL);
12380Sstevel@tonic-gate 		error = USB_FAILURE;
12390Sstevel@tonic-gate 		break;
12400Sstevel@tonic-gate 	}
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
12430Sstevel@tonic-gate 	    "hid_wput: End");
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	return (DDI_SUCCESS);
12460Sstevel@tonic-gate }
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate /*
12500Sstevel@tonic-gate  * hid_wsrv :
12510Sstevel@tonic-gate  *	Write service routine for hid. When a message arrives through
12520Sstevel@tonic-gate  *	hid_wput(), it is kept in write queue to be serviced later.
12530Sstevel@tonic-gate  */
12540Sstevel@tonic-gate static int
hid_wsrv(queue_t * q)12550Sstevel@tonic-gate hid_wsrv(queue_t *q)
12560Sstevel@tonic-gate {
125710153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)q->q_ptr;
12580Sstevel@tonic-gate 	int		error;
12590Sstevel@tonic-gate 	mblk_t		*mp;
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
12620Sstevel@tonic-gate 	    "hid_wsrv: Begin");
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
12650Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
12660Sstevel@tonic-gate 	    "hid_wsrv: dev_state: %s",
12670Sstevel@tonic-gate 	    usb_str_dev_state(hidp->hid_dev_state));
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate 	/*
12700Sstevel@tonic-gate 	 * raise power if we are powered down. It is OK to block here since
12710Sstevel@tonic-gate 	 * we have a separate thread to process this STREAM
12720Sstevel@tonic-gate 	 */
12730Sstevel@tonic-gate 	if (hidp->hid_dev_state == USB_DEV_PWRED_DOWN) {
12740Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
12750Sstevel@tonic-gate 		(void) pm_raise_power(hidp->hid_dip, 0, USB_DEV_OS_FULL_PWR);
12760Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
12770Sstevel@tonic-gate 	}
12780Sstevel@tonic-gate 
12790Sstevel@tonic-gate 	/*
12800Sstevel@tonic-gate 	 * continue servicing all the M_CTL's till the queue is empty
12810Sstevel@tonic-gate 	 * or the device gets disconnected or till a hid_close()
12820Sstevel@tonic-gate 	 */
12830Sstevel@tonic-gate 	while ((hidp->hid_dev_state == USB_DEV_ONLINE) &&
128410153SAaron.Zang@Sun.COM 	    (HID_STREAMS_FLAG(q, hidp) != HID_STREAMS_DISMANTLING) &&
12850Sstevel@tonic-gate 	    ((mp = getq(q)) != NULL)) {
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate 		/* Send a message down */
12880Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
12890Sstevel@tonic-gate 		error = hid_mctl_receive(q, mp);
12900Sstevel@tonic-gate 		switch (error) {
12910Sstevel@tonic-gate 		case HID_ENQUEUE:
12920Sstevel@tonic-gate 			/* put this mblk back on q to preserve order */
12930Sstevel@tonic-gate 			(void) putbq(q, mp);
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 			break;
12960Sstevel@tonic-gate 		case HID_INPROGRESS:
12970Sstevel@tonic-gate 			/* request has been queued to the device */
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 			break;
13000Sstevel@tonic-gate 		case HID_SUCCESS:
13010Sstevel@tonic-gate 		case HID_FAILURE:
13020Sstevel@tonic-gate 		default:
13030Sstevel@tonic-gate 			hid_pm_idle_component(hidp);
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 			break;
13060Sstevel@tonic-gate 		}
13070Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
13080Sstevel@tonic-gate 	}
13090Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
13100Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
13110Sstevel@tonic-gate 	    "hid_wsrv: End");
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	return (DDI_SUCCESS);
13140Sstevel@tonic-gate }
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate /*
13180Sstevel@tonic-gate  * hid_power:
13190Sstevel@tonic-gate  *	power entry point
13200Sstevel@tonic-gate  */
13210Sstevel@tonic-gate static int
hid_power(dev_info_t * dip,int comp,int level)13220Sstevel@tonic-gate hid_power(dev_info_t *dip, int comp, int level)
13230Sstevel@tonic-gate {
13240Sstevel@tonic-gate 	int		instance = ddi_get_instance(dip);
13250Sstevel@tonic-gate 	hid_state_t	*hidp;
13260Sstevel@tonic-gate 	hid_power_t	*hidpm;
13270Sstevel@tonic-gate 	int		retval;
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 	hidp = ddi_get_soft_state(hid_statep, instance);
13300Sstevel@tonic-gate 
13310Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_PM, hidp->hid_log_handle, "hid_power:"
13320Sstevel@tonic-gate 	    " hid_state: comp=%d level=%d", comp, level);
13330Sstevel@tonic-gate 
13340Sstevel@tonic-gate 	/* check if we are transitioning to a legal power level */
13350Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
13360Sstevel@tonic-gate 	hidpm = hidp->hid_pm;
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate 	if (USB_DEV_PWRSTATE_OK(hidpm->hid_pwr_states, level)) {
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_PM, hidp->hid_log_handle,
13410Sstevel@tonic-gate 		    "hid_power: illegal level=%d hid_pwr_states=%d",
13420Sstevel@tonic-gate 		    level, hidpm->hid_pwr_states);
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
13450Sstevel@tonic-gate 
13460Sstevel@tonic-gate 		return (DDI_FAILURE);
13470Sstevel@tonic-gate 	}
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate 	switch (level) {
13500Sstevel@tonic-gate 	case USB_DEV_OS_PWR_OFF:
13510Sstevel@tonic-gate 		retval = hid_pwrlvl0(hidp);
13520Sstevel@tonic-gate 		break;
13530Sstevel@tonic-gate 	case USB_DEV_OS_PWR_1:
13540Sstevel@tonic-gate 		retval = hid_pwrlvl1(hidp);
13550Sstevel@tonic-gate 		break;
13560Sstevel@tonic-gate 	case USB_DEV_OS_PWR_2:
13570Sstevel@tonic-gate 		retval = hid_pwrlvl2(hidp);
13580Sstevel@tonic-gate 		break;
13590Sstevel@tonic-gate 	case USB_DEV_OS_FULL_PWR:
13600Sstevel@tonic-gate 		retval = hid_pwrlvl3(hidp);
13610Sstevel@tonic-gate 		break;
13620Sstevel@tonic-gate 	default:
13630Sstevel@tonic-gate 		retval = USB_FAILURE;
13640Sstevel@tonic-gate 		break;
13650Sstevel@tonic-gate 	}
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 	return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
13700Sstevel@tonic-gate }
13710Sstevel@tonic-gate 
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate /*
13740Sstevel@tonic-gate  * hid_interrupt_pipe_callback:
13750Sstevel@tonic-gate  *	Callback function for the hid intr pipe. This function is called by
13760Sstevel@tonic-gate  *	USBA when a buffer has been filled. This driver does not cook the data,
13770Sstevel@tonic-gate  *	it just sends the message up.
13780Sstevel@tonic-gate  */
13790Sstevel@tonic-gate static void
hid_interrupt_pipe_callback(usb_pipe_handle_t pipe,usb_intr_req_t * req)13800Sstevel@tonic-gate hid_interrupt_pipe_callback(usb_pipe_handle_t pipe, usb_intr_req_t *req)
13810Sstevel@tonic-gate {
13820Sstevel@tonic-gate 	hid_state_t *hidp = (hid_state_t *)req->intr_client_private;
13830Sstevel@tonic-gate 	queue_t	*q;
13840Sstevel@tonic-gate 
13850Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
13866898Sfb209375 	    "hid_interrupt_pipe_callback: ph = 0x%p req = 0x%p",
13876898Sfb209375 	    (void *)pipe, (void *)req);
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 	hid_pm_busy_component(hidp);
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
13920Sstevel@tonic-gate 
13930Sstevel@tonic-gate 	/*
13940Sstevel@tonic-gate 	 * If hid_close() is in progress, we shouldn't try accessing queue
13950Sstevel@tonic-gate 	 * Otherwise indicate that a putnext is going to happen, so
13960Sstevel@tonic-gate 	 * if close after this, that should wait for the putnext to finish.
13970Sstevel@tonic-gate 	 */
139810153SAaron.Zang@Sun.COM 	if (HID_STREAMS_FLAG(hidp->hid_inuse_rq, hidp) ==
139910153SAaron.Zang@Sun.COM 	    HID_STREAMS_OPEN) {
14000Sstevel@tonic-gate 		/*
14010Sstevel@tonic-gate 		 * Check if data can be put to the next queue.
14020Sstevel@tonic-gate 		 */
140310153SAaron.Zang@Sun.COM 		if (!canputnext(hidp->hid_inuse_rq)) {
14040Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
14050Sstevel@tonic-gate 			    "Buffer flushed when overflowed.");
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 			/* Flush the queue above */
140810153SAaron.Zang@Sun.COM 			hid_flush(hidp->hid_inuse_rq);
14090Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
14100Sstevel@tonic-gate 		} else {
141110153SAaron.Zang@Sun.COM 			q = hidp->hid_inuse_rq;
14120Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 			/* Put data upstream */
14150Sstevel@tonic-gate 			putnext(q, req->intr_data);
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 			/* usb_free_intr_req should not free data */
14180Sstevel@tonic-gate 			req->intr_data = NULL;
14190Sstevel@tonic-gate 		}
14200Sstevel@tonic-gate 	} else {
14210Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
14220Sstevel@tonic-gate 	}
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 	/* free request and data */
14250Sstevel@tonic-gate 	usb_free_intr_req(req);
14260Sstevel@tonic-gate 	hid_pm_idle_component(hidp);
14270Sstevel@tonic-gate }
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate /*
14310Sstevel@tonic-gate  * hid_default_pipe_callback :
14320Sstevel@tonic-gate  *	Callback routine for the asynchronous control transfer
14330Sstevel@tonic-gate  *	Called from hid_send_async_ctrl_request() where we open
14340Sstevel@tonic-gate  *	the pipe in exclusive mode
14350Sstevel@tonic-gate  */
14360Sstevel@tonic-gate static void
hid_default_pipe_callback(usb_pipe_handle_t pipe,usb_ctrl_req_t * req)14370Sstevel@tonic-gate hid_default_pipe_callback(usb_pipe_handle_t pipe, usb_ctrl_req_t *req)
14380Sstevel@tonic-gate {
14390Sstevel@tonic-gate 	hid_default_pipe_arg_t *hid_default_pipe_arg =
14400Sstevel@tonic-gate 	    (hid_default_pipe_arg_t *)req->ctrl_client_private;
14419432SPengcheng.Chen@Sun.COM 	queue_t		*wq = hid_default_pipe_arg->hid_default_pipe_arg_queue;
14429432SPengcheng.Chen@Sun.COM 	queue_t		*rq = RD(wq);
144310153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)rq->q_ptr;
14440Sstevel@tonic-gate 	mblk_t		*mctl_mp;
14450Sstevel@tonic-gate 	mblk_t		*data = NULL;
14460Sstevel@tonic-gate 
14470Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
14480Sstevel@tonic-gate 	    "hid_default_pipe_callback: "
14496898Sfb209375 	    "ph = 0x%p, req = 0x%p, data= 0x%p",
14506898Sfb209375 	    (void *)pipe, (void *)req, (void *)data);
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate 	ASSERT((req->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
14530Sstevel@tonic-gate 
14540Sstevel@tonic-gate 	if (req->ctrl_data) {
14550Sstevel@tonic-gate 		data = req->ctrl_data;
14560Sstevel@tonic-gate 		req->ctrl_data = NULL;
14570Sstevel@tonic-gate 	}
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 	/*
14600Sstevel@tonic-gate 	 * Free the b_cont of the original message that was sent down.
14610Sstevel@tonic-gate 	 */
14620Sstevel@tonic-gate 	mctl_mp = hid_default_pipe_arg->hid_default_pipe_arg_mblk;
14630Sstevel@tonic-gate 	freemsg(mctl_mp->b_cont);
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 	/* chain the mblk received to the original & send it up */
14660Sstevel@tonic-gate 	mctl_mp->b_cont = data;
14679432SPengcheng.Chen@Sun.COM 
14689432SPengcheng.Chen@Sun.COM 	if (canputnext(rq)) {
14699432SPengcheng.Chen@Sun.COM 		putnext(rq, mctl_mp);
14700Sstevel@tonic-gate 	} else {
14710Sstevel@tonic-gate 		freemsg(mctl_mp); /* avoid leak */
14720Sstevel@tonic-gate 	}
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate 	/*
14750Sstevel@tonic-gate 	 * Free the argument for the asynchronous callback
14760Sstevel@tonic-gate 	 */
14770Sstevel@tonic-gate 	kmem_free(hid_default_pipe_arg, sizeof (hid_default_pipe_arg_t));
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 	/*
14800Sstevel@tonic-gate 	 * Free the control pipe request structure.
14810Sstevel@tonic-gate 	 */
14820Sstevel@tonic-gate 	usb_free_ctrl_req(req);
14830Sstevel@tonic-gate 
14840Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
14850Sstevel@tonic-gate 	hidp->hid_default_pipe_req--;
14860Sstevel@tonic-gate 	ASSERT(hidp->hid_default_pipe_req >= 0);
14870Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
14880Sstevel@tonic-gate 
14890Sstevel@tonic-gate 	hid_pm_idle_component(hidp);
14909432SPengcheng.Chen@Sun.COM 	qenable(wq);
14910Sstevel@tonic-gate }
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate /*
14950Sstevel@tonic-gate  * hid_interrupt_pipe_exception_callback:
14960Sstevel@tonic-gate  *	Exception callback routine for interrupt pipe. If there is any data,
14970Sstevel@tonic-gate  *	destroy it. No threads are waiting for the exception callback.
14980Sstevel@tonic-gate  */
14990Sstevel@tonic-gate /*ARGSUSED*/
15000Sstevel@tonic-gate static void
hid_interrupt_pipe_exception_callback(usb_pipe_handle_t pipe,usb_intr_req_t * req)15010Sstevel@tonic-gate hid_interrupt_pipe_exception_callback(usb_pipe_handle_t pipe,
15020Sstevel@tonic-gate     usb_intr_req_t *req)
15030Sstevel@tonic-gate {
15040Sstevel@tonic-gate 	hid_state_t	*hidp = (hid_state_t *)req->intr_client_private;
15050Sstevel@tonic-gate 	mblk_t		*data = req->intr_data;
15060Sstevel@tonic-gate 	usb_cb_flags_t	flags = req->intr_cb_flags;
15070Sstevel@tonic-gate 	int		rval;
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
15100Sstevel@tonic-gate 	    "hid_interrupt_pipe_exception_callback: "
15110Sstevel@tonic-gate 	    "completion_reason = 0x%x, data = 0x%p, flag = 0x%x",
15120Sstevel@tonic-gate 	    req->intr_completion_reason, (void *)data, req->intr_cb_flags);
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate 	ASSERT((req->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
15150Sstevel@tonic-gate 
15160Sstevel@tonic-gate 	if (((flags & USB_CB_FUNCTIONAL_STALL) != 0) &&
15170Sstevel@tonic-gate 	    ((flags & USB_CB_STALL_CLEARED) == 0)) {
15180Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL,
15190Sstevel@tonic-gate 		    hidp->hid_log_handle,
15200Sstevel@tonic-gate 		    "hid_interrupt_pipe_exception_callback: "
15210Sstevel@tonic-gate 		    "unable to clear stall.  flags = 0x%x",
15220Sstevel@tonic-gate 		    req->intr_cb_flags);
15230Sstevel@tonic-gate 	}
15240Sstevel@tonic-gate 
15250Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
15260Sstevel@tonic-gate 
15270Sstevel@tonic-gate 	switch (req->intr_completion_reason) {
15280Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
15290Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
15300Sstevel@tonic-gate 	default:
15310Sstevel@tonic-gate 
15320Sstevel@tonic-gate 		break;
15330Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
15340Sstevel@tonic-gate 	case USB_CR_NO_RESOURCES:
15350Sstevel@tonic-gate 		if ((hidp->hid_dev_state == USB_DEV_ONLINE) &&
15360Sstevel@tonic-gate 		    ((rval = hid_start_intr_polling(hidp)) !=
15370Sstevel@tonic-gate 		    USB_SUCCESS)) {
15380Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
15390Sstevel@tonic-gate 			    "unable to restart interrupt poll. rval = %d",
15400Sstevel@tonic-gate 			    rval);
15410Sstevel@tonic-gate 		}
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 		break;
15440Sstevel@tonic-gate 	}
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
15470Sstevel@tonic-gate 
15480Sstevel@tonic-gate 	usb_free_intr_req(req);
15490Sstevel@tonic-gate }
15500Sstevel@tonic-gate 
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate /*
15530Sstevel@tonic-gate  * hid_default_pipe_exception_callback:
15540Sstevel@tonic-gate  *	Exception callback routine for default pipe.
15550Sstevel@tonic-gate  */
15560Sstevel@tonic-gate /*ARGSUSED*/
15570Sstevel@tonic-gate static void
hid_default_pipe_exception_callback(usb_pipe_handle_t pipe,usb_ctrl_req_t * req)15580Sstevel@tonic-gate hid_default_pipe_exception_callback(usb_pipe_handle_t pipe,
15590Sstevel@tonic-gate     usb_ctrl_req_t *req)
15600Sstevel@tonic-gate {
15610Sstevel@tonic-gate 	hid_default_pipe_arg_t *hid_default_pipe_arg =
15620Sstevel@tonic-gate 	    (hid_default_pipe_arg_t *)req->ctrl_client_private;
15639432SPengcheng.Chen@Sun.COM 	queue_t		*wq = hid_default_pipe_arg->hid_default_pipe_arg_queue;
15649432SPengcheng.Chen@Sun.COM 	queue_t		*rq = RD(wq);
156510153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)rq->q_ptr;
15660Sstevel@tonic-gate 	usb_cr_t	ctrl_completion_reason = req->ctrl_completion_reason;
15679432SPengcheng.Chen@Sun.COM 	mblk_t		*mp, *data = NULL;
15680Sstevel@tonic-gate 
15690Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
15700Sstevel@tonic-gate 	    "hid_default_pipe_exception_callback: "
15710Sstevel@tonic-gate 	    "completion_reason = 0x%x, data = 0x%p, flag = 0x%x",
15720Sstevel@tonic-gate 	    ctrl_completion_reason, (void *)data, req->ctrl_cb_flags);
15730Sstevel@tonic-gate 
15740Sstevel@tonic-gate 	ASSERT((req->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
15750Sstevel@tonic-gate 
15769432SPengcheng.Chen@Sun.COM 	mp = hid_default_pipe_arg->hid_default_pipe_arg_mblk;
15770Sstevel@tonic-gate 
15780Sstevel@tonic-gate 	/*
15790Sstevel@tonic-gate 	 * Pass an error message up. Reuse existing mblk.
15800Sstevel@tonic-gate 	 */
15819432SPengcheng.Chen@Sun.COM 	if (canputnext(rq)) {
15820Sstevel@tonic-gate 		mp->b_datap->db_type = M_ERROR;
15830Sstevel@tonic-gate 		mp->b_rptr = mp->b_datap->db_base;
15840Sstevel@tonic-gate 		mp->b_wptr = mp->b_rptr + sizeof (char);
15850Sstevel@tonic-gate 		*mp->b_rptr = EIO;
15869432SPengcheng.Chen@Sun.COM 		putnext(rq, mp);
15870Sstevel@tonic-gate 	} else {
15889432SPengcheng.Chen@Sun.COM 		freemsg(mp);
15890Sstevel@tonic-gate 	}
15909432SPengcheng.Chen@Sun.COM 
15910Sstevel@tonic-gate 	kmem_free(hid_default_pipe_arg, sizeof (hid_default_pipe_arg_t));
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
15940Sstevel@tonic-gate 	hidp->hid_default_pipe_req--;
15950Sstevel@tonic-gate 	ASSERT(hidp->hid_default_pipe_req >= 0);
15960Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
15970Sstevel@tonic-gate 
15989432SPengcheng.Chen@Sun.COM 	qenable(wq);
15990Sstevel@tonic-gate 	usb_free_ctrl_req(req);
16000Sstevel@tonic-gate 	hid_pm_idle_component(hidp);
16010Sstevel@tonic-gate }
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate /*
16050Sstevel@tonic-gate  * event handling:
16060Sstevel@tonic-gate  *
16070Sstevel@tonic-gate  * hid_reconnect_event_callback:
16080Sstevel@tonic-gate  *	the device was disconnected but this instance not detached, probably
16090Sstevel@tonic-gate  *	because the device was busy
16100Sstevel@tonic-gate  *
16110Sstevel@tonic-gate  *	If the same device, continue with restoring state
16120Sstevel@tonic-gate  */
16130Sstevel@tonic-gate static int
hid_restore_state_event_callback(dev_info_t * dip)16140Sstevel@tonic-gate hid_restore_state_event_callback(dev_info_t *dip)
16150Sstevel@tonic-gate {
16160Sstevel@tonic-gate 	hid_state_t	*hidp = (hid_state_t *)ddi_get_soft_state(hid_statep,
16176898Sfb209375 	    ddi_get_instance(dip));
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	ASSERT(hidp != NULL);
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_EVENTS, hidp->hid_log_handle,
16226898Sfb209375 	    "hid_restore_state_event_callback: dip=0x%p", (void *)dip);
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 	hid_restore_device_state(dip, hidp);
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 	return (USB_SUCCESS);
16270Sstevel@tonic-gate }
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate /*
16310Sstevel@tonic-gate  * hid_cpr_suspend
16320Sstevel@tonic-gate  *	Fail suspend if we can't finish outstanding i/o activity.
16330Sstevel@tonic-gate  */
16340Sstevel@tonic-gate static int
hid_cpr_suspend(hid_state_t * hidp)16350Sstevel@tonic-gate hid_cpr_suspend(hid_state_t *hidp)
16360Sstevel@tonic-gate {
16370Sstevel@tonic-gate 	int		rval, prev_state;
16380Sstevel@tonic-gate 	int		retval = USB_FAILURE;
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
16416898Sfb209375 	    "hid_cpr_suspend: dip=0x%p", (void *)hidp->hid_dip);
16420Sstevel@tonic-gate 
16430Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
16440Sstevel@tonic-gate 	switch (hidp->hid_dev_state) {
16450Sstevel@tonic-gate 	case USB_DEV_ONLINE:
16460Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
16470Sstevel@tonic-gate 		prev_state = hidp->hid_dev_state;
16480Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_SUSPENDED;
16490Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 		/* drain all request outstanding on the default control pipe */
16520Sstevel@tonic-gate 		rval = usb_pipe_drain_reqs(hidp->hid_dip,
16530Sstevel@tonic-gate 		    hidp->hid_default_pipe, hid_default_pipe_drain_timeout,
16540Sstevel@tonic-gate 		    USB_FLAGS_SLEEP, NULL, 0);
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 		/* fail checkpoint if we haven't finished the job yet */
16570Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
16580Sstevel@tonic-gate 		if ((rval != USB_SUCCESS) || (hidp->hid_default_pipe_req > 0)) {
16590Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
16600Sstevel@tonic-gate 			    "hid_cpr_suspend: "
16610Sstevel@tonic-gate 			    "device busy - can't checkpoint");
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 			/* fall back to previous state */
16640Sstevel@tonic-gate 			hidp->hid_dev_state = prev_state;
16650Sstevel@tonic-gate 		} else {
16660Sstevel@tonic-gate 			retval = USB_SUCCESS;
16670Sstevel@tonic-gate 			hid_save_device_state(hidp);
16680Sstevel@tonic-gate 		}
16690Sstevel@tonic-gate 
16700Sstevel@tonic-gate 		break;
167112419SFei.Feng@Sun.COM 	case USB_DEV_DISCONNECTED:
167212419SFei.Feng@Sun.COM 		hidp->hid_dev_state = USB_DEV_SUSPENDED;
167312419SFei.Feng@Sun.COM 		hid_save_device_state(hidp);
167412419SFei.Feng@Sun.COM 		retval = USB_SUCCESS;
167512419SFei.Feng@Sun.COM 		break;
16760Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
16770Sstevel@tonic-gate 	default:
16780Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
16790Sstevel@tonic-gate 		    "hid_cpr_suspend: Illegal dev state: %d",
16800Sstevel@tonic-gate 		    hidp->hid_dev_state);
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 		break;
16830Sstevel@tonic-gate 	}
16840Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 	return (retval);
16870Sstevel@tonic-gate }
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 
16900Sstevel@tonic-gate static void
hid_cpr_resume(hid_state_t * hidp)16910Sstevel@tonic-gate hid_cpr_resume(hid_state_t *hidp)
16920Sstevel@tonic-gate {
16930Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
16946898Sfb209375 	    "hid_cpr_resume: dip=0x%p", (void *)hidp->hid_dip);
16950Sstevel@tonic-gate 
16960Sstevel@tonic-gate 	hid_restore_device_state(hidp->hid_dip, hidp);
16970Sstevel@tonic-gate }
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate /*
17010Sstevel@tonic-gate  * hid_disconnect_event_callback:
17020Sstevel@tonic-gate  *	The device has been disconnected. We either wait for
17030Sstevel@tonic-gate  *	detach or a reconnect event. Close all pipes and timeouts.
17040Sstevel@tonic-gate  */
17050Sstevel@tonic-gate static int
hid_disconnect_event_callback(dev_info_t * dip)17060Sstevel@tonic-gate hid_disconnect_event_callback(dev_info_t *dip)
17070Sstevel@tonic-gate {
17080Sstevel@tonic-gate 	hid_state_t	*hidp;
17099432SPengcheng.Chen@Sun.COM 	mblk_t		*mp;
17100Sstevel@tonic-gate 
17110Sstevel@tonic-gate 	hidp = (hid_state_t *)ddi_get_soft_state(hid_statep,
17120Sstevel@tonic-gate 	    ddi_get_instance(dip));
17130Sstevel@tonic-gate 	ASSERT(hidp != NULL);
17140Sstevel@tonic-gate 
17150Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
17166898Sfb209375 	    "hid_disconnect_event_callback: dip=0x%p", (void *)dip);
17170Sstevel@tonic-gate 
17180Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
17190Sstevel@tonic-gate 	switch (hidp->hid_dev_state) {
17200Sstevel@tonic-gate 	case USB_DEV_ONLINE:
17210Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
17220Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_DISCONNECTED;
172310153SAaron.Zang@Sun.COM 		if (HID_IS_OPEN(hidp)) {
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
17260Sstevel@tonic-gate 			    "busy device has been disconnected");
17270Sstevel@tonic-gate 		}
17280Sstevel@tonic-gate 		hid_save_device_state(hidp);
17290Sstevel@tonic-gate 
17309432SPengcheng.Chen@Sun.COM 		/*
17319432SPengcheng.Chen@Sun.COM 		 * Notify applications about device removal, this only
17329432SPengcheng.Chen@Sun.COM 		 * applies to an external (aka. physical) open. For an
17339432SPengcheng.Chen@Sun.COM 		 * internal open, consconfig_dacf closes the queue.
17349432SPengcheng.Chen@Sun.COM 		 */
173510153SAaron.Zang@Sun.COM 		if (hidp->hid_external_flag == HID_STREAMS_OPEN) {
173610153SAaron.Zang@Sun.COM 			queue_t *q = hidp->hid_external_rq;
173710153SAaron.Zang@Sun.COM 			mutex_exit(&hidp->hid_mutex);
173810153SAaron.Zang@Sun.COM 			mp = allocb(sizeof (uchar_t), BPRI_HI);
173910153SAaron.Zang@Sun.COM 			if (mp != NULL) {
174010153SAaron.Zang@Sun.COM 				mp->b_datap->db_type = M_ERROR;
174110153SAaron.Zang@Sun.COM 				mp->b_rptr = mp->b_datap->db_base;
174210153SAaron.Zang@Sun.COM 				mp->b_wptr = mp->b_rptr + sizeof (char);
174310153SAaron.Zang@Sun.COM 				*mp->b_rptr = ENODEV;
174410153SAaron.Zang@Sun.COM 				putnext(q, mp);
17459432SPengcheng.Chen@Sun.COM 			}
174610153SAaron.Zang@Sun.COM 			mutex_enter(&hidp->hid_mutex);
17479432SPengcheng.Chen@Sun.COM 		}
17489432SPengcheng.Chen@Sun.COM 
17490Sstevel@tonic-gate 		break;
17500Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
17510Sstevel@tonic-gate 		/* we remain suspended */
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 		break;
17540Sstevel@tonic-gate 	default:
17550Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
17560Sstevel@tonic-gate 		    "hid_disconnect_event_callback: Illegal dev state: %d",
17570Sstevel@tonic-gate 		    hidp->hid_dev_state);
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 		break;
17600Sstevel@tonic-gate 	}
17610Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
17620Sstevel@tonic-gate 
17630Sstevel@tonic-gate 	return (USB_SUCCESS);
17640Sstevel@tonic-gate }
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate 
17670Sstevel@tonic-gate /*
17680Sstevel@tonic-gate  * hid_power_change_callback:
17690Sstevel@tonic-gate  *	Async callback function to notify pm_raise_power completion
17700Sstevel@tonic-gate  *	after hid_power entry point is called.
17710Sstevel@tonic-gate  */
17720Sstevel@tonic-gate static void
hid_power_change_callback(void * arg,int rval)17730Sstevel@tonic-gate hid_power_change_callback(void *arg, int rval)
17740Sstevel@tonic-gate {
17750Sstevel@tonic-gate 	hid_state_t	*hidp;
17760Sstevel@tonic-gate 	queue_t		*wq;
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate 	hidp = (hid_state_t *)arg;
17790Sstevel@tonic-gate 
17800Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
17810Sstevel@tonic-gate 	    "hid_power_change_callback - rval: %d", rval);
17820Sstevel@tonic-gate 
17830Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
17840Sstevel@tonic-gate 	hidp->hid_pm->hid_raise_power = B_FALSE;
17850Sstevel@tonic-gate 
17860Sstevel@tonic-gate 	if (hidp->hid_dev_state == USB_DEV_ONLINE) {
178710153SAaron.Zang@Sun.COM 		wq = WR(hidp->hid_inuse_rq);
17880Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
17890Sstevel@tonic-gate 
17900Sstevel@tonic-gate 		qenable(wq);
17910Sstevel@tonic-gate 
17920Sstevel@tonic-gate 	} else {
17930Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
17940Sstevel@tonic-gate 	}
17950Sstevel@tonic-gate }
17960Sstevel@tonic-gate 
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate /*
17990Sstevel@tonic-gate  * hid_parse_hid_descr:
18000Sstevel@tonic-gate  *	Parse the hid descriptor, check after interface and after
18010Sstevel@tonic-gate  *	endpoint descriptor
18020Sstevel@tonic-gate  */
18030Sstevel@tonic-gate static size_t
hid_parse_hid_descr(usb_hid_descr_t * ret_descr,size_t ret_buf_len,usb_alt_if_data_t * altif_data,usb_ep_data_t * ep_data)18040Sstevel@tonic-gate hid_parse_hid_descr(
18050Sstevel@tonic-gate 	usb_hid_descr_t		*ret_descr,
18060Sstevel@tonic-gate 	size_t			ret_buf_len,
18070Sstevel@tonic-gate 	usb_alt_if_data_t	*altif_data,
18080Sstevel@tonic-gate 	usb_ep_data_t		*ep_data)
18090Sstevel@tonic-gate {
18100Sstevel@tonic-gate 	usb_cvs_data_t *cvs;
18110Sstevel@tonic-gate 	int		which_cvs;
18120Sstevel@tonic-gate 
18130Sstevel@tonic-gate 	for (which_cvs = 0; which_cvs < altif_data->altif_n_cvs; which_cvs++) {
18140Sstevel@tonic-gate 		cvs = &altif_data->altif_cvs[which_cvs];
18150Sstevel@tonic-gate 		if (cvs->cvs_buf == NULL) {
18160Sstevel@tonic-gate 			continue;
18170Sstevel@tonic-gate 		}
18180Sstevel@tonic-gate 		if (cvs->cvs_buf[1] == USB_DESCR_TYPE_HID) {
18190Sstevel@tonic-gate 			return (usb_parse_data("ccscccs",
18206898Sfb209375 			    cvs->cvs_buf, cvs->cvs_buf_len,
18216898Sfb209375 			    (void *)ret_descr,
18226898Sfb209375 			    (size_t)ret_buf_len));
18230Sstevel@tonic-gate 		}
18240Sstevel@tonic-gate 	}
18250Sstevel@tonic-gate 
18260Sstevel@tonic-gate 	/* now try after endpoint */
18270Sstevel@tonic-gate 	for (which_cvs = 0; which_cvs < ep_data->ep_n_cvs; which_cvs++) {
18280Sstevel@tonic-gate 		cvs = &ep_data->ep_cvs[which_cvs];
18290Sstevel@tonic-gate 		if (cvs->cvs_buf == NULL) {
18300Sstevel@tonic-gate 			continue;
18310Sstevel@tonic-gate 		}
18320Sstevel@tonic-gate 		if (cvs->cvs_buf[1] == USB_DESCR_TYPE_HID) {
18330Sstevel@tonic-gate 			return (usb_parse_data("ccscccs",
18346898Sfb209375 			    cvs->cvs_buf, cvs->cvs_buf_len,
18356898Sfb209375 			    (void *)ret_descr,
18366898Sfb209375 			    (size_t)ret_buf_len));
18370Sstevel@tonic-gate 		}
18380Sstevel@tonic-gate 	}
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 	return (USB_PARSE_ERROR);
18410Sstevel@tonic-gate }
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 
18440Sstevel@tonic-gate /*
18450Sstevel@tonic-gate  * hid_parse_hid_descr_failure:
18460Sstevel@tonic-gate  *	If parsing of hid descriptor failed and the device is
18470Sstevel@tonic-gate  *	a keyboard or mouse, use predefined length and packet size.
18480Sstevel@tonic-gate  */
18490Sstevel@tonic-gate static int
hid_parse_hid_descr_failure(hid_state_t * hidp)18500Sstevel@tonic-gate hid_parse_hid_descr_failure(hid_state_t	*hidp)
18510Sstevel@tonic-gate {
18520Sstevel@tonic-gate 	/*
18530Sstevel@tonic-gate 	 * Parsing hid descriptor failed, probably because the
18540Sstevel@tonic-gate 	 * device did not return a valid hid descriptor. Check to
18550Sstevel@tonic-gate 	 * see if this is a keyboard or mouse. If so, use the
18560Sstevel@tonic-gate 	 * predefined hid descriptor length and packet size.
18570Sstevel@tonic-gate 	 * Otherwise, detach and return failure.
18580Sstevel@tonic-gate 	 */
18590Sstevel@tonic-gate 	USB_DPRINTF_L1(PRINT_MASK_ATTA, hidp->hid_log_handle,
18600Sstevel@tonic-gate 	    "Parsing of hid descriptor failed");
18610Sstevel@tonic-gate 
18620Sstevel@tonic-gate 	if (hidp->hid_if_descr.bInterfaceProtocol == KEYBOARD_PROTOCOL) {
18630Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
18640Sstevel@tonic-gate 		    "Set hid descriptor length to predefined "
18650Sstevel@tonic-gate 		    "USB_KB_HID_DESCR_LENGTH for keyboard.");
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate 		/* device is a keyboard */
18680Sstevel@tonic-gate 		hidp->hid_hid_descr.wReportDescriptorLength =
18696898Sfb209375 		    USB_KB_HID_DESCR_LENGTH;
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 		hidp->hid_packet_size = USBKPSZ;
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 	} else if (hidp->hid_if_descr.bInterfaceProtocol ==
18740Sstevel@tonic-gate 	    MOUSE_PROTOCOL) {
18750Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
18760Sstevel@tonic-gate 		    "Set hid descriptor length to predefined "
18770Sstevel@tonic-gate 		    "USB_MS_HID_DESCR_LENGTH for mouse.");
18780Sstevel@tonic-gate 
18790Sstevel@tonic-gate 		/* device is a mouse */
18800Sstevel@tonic-gate 		hidp->hid_hid_descr.wReportDescriptorLength =
18816898Sfb209375 		    USB_MS_HID_DESCR_LENGTH;
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate 		hidp->hid_packet_size = USBMSSZ;
18840Sstevel@tonic-gate 	} else {
18850Sstevel@tonic-gate 
18860Sstevel@tonic-gate 		return (USB_FAILURE);
18870Sstevel@tonic-gate 	}
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate 	return (USB_SUCCESS);
18900Sstevel@tonic-gate }
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate 
18930Sstevel@tonic-gate /*
18940Sstevel@tonic-gate  * hid_handle_report_descriptor:
18950Sstevel@tonic-gate  *	Get the report descriptor, call hidparser routine to parse
18960Sstevel@tonic-gate  *	it and query the hidparser tree to get the packet size
18970Sstevel@tonic-gate  */
18980Sstevel@tonic-gate static int
hid_handle_report_descriptor(hid_state_t * hidp,int interface)18990Sstevel@tonic-gate hid_handle_report_descriptor(hid_state_t	*hidp,
19000Sstevel@tonic-gate 				int		interface)
19010Sstevel@tonic-gate {
19020Sstevel@tonic-gate 	usb_cr_t		completion_reason;
19030Sstevel@tonic-gate 	usb_cb_flags_t		cb_flags;
19040Sstevel@tonic-gate 	mblk_t			*data = NULL;
19050Sstevel@tonic-gate 	hidparser_packet_info_t	hpack;
19060Sstevel@tonic-gate 	int			i;
19070Sstevel@tonic-gate 	usb_ctrl_setup_t setup = {
19080Sstevel@tonic-gate 	    USB_DEV_REQ_DEV_TO_HOST |	/* bmRequestType */
19096898Sfb209375 	    USB_DEV_REQ_RCPT_IF,
19100Sstevel@tonic-gate 	    USB_REQ_GET_DESCR,		/* bRequest */
19110Sstevel@tonic-gate 	    USB_CLASS_DESCR_TYPE_REPORT, /* wValue */
19120Sstevel@tonic-gate 	    0,				/* wIndex: interface, fill in later */
19130Sstevel@tonic-gate 	    0,				/* wLength, fill in later  */
19140Sstevel@tonic-gate 	    0				/* attributes */
19156898Sfb209375 	    };
19160Sstevel@tonic-gate 
19170Sstevel@tonic-gate 	/*
19180Sstevel@tonic-gate 	 * Parsing hid desciptor was successful earlier.
19190Sstevel@tonic-gate 	 * Get Report Descriptor
19200Sstevel@tonic-gate 	 */
19210Sstevel@tonic-gate 	setup.wIndex = (uint16_t)interface;
19220Sstevel@tonic-gate 	setup.wLength = hidp->hid_hid_descr.wReportDescriptorLength;
19230Sstevel@tonic-gate 	if (usb_pipe_ctrl_xfer_wait(hidp->hid_default_pipe,
19240Sstevel@tonic-gate 	    &setup,
19250Sstevel@tonic-gate 	    &data,				/* data */
19260Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
19270Sstevel@tonic-gate 
1928978Sfrits 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
19296898Sfb209375 		    "Failed to receive the Report Descriptor");
19300Sstevel@tonic-gate 		freemsg(data);
19310Sstevel@tonic-gate 
19320Sstevel@tonic-gate 		return (USB_FAILURE);
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 	} else {
19350Sstevel@tonic-gate 		int n =  hidp->hid_hid_descr.wReportDescriptorLength;
19360Sstevel@tonic-gate 
19370Sstevel@tonic-gate 		ASSERT(data);
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 		/* Print the report descriptor */
19400Sstevel@tonic-gate 		for (i = 0; i < n; i++) {
19410Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ATTA, hidp->hid_log_handle,
19426898Sfb209375 			    "Index = %d\tvalue =0x%x", i,
19436898Sfb209375 			    (int)(data->b_rptr[i]));
19440Sstevel@tonic-gate 		}
19450Sstevel@tonic-gate 
19460Sstevel@tonic-gate 		/* Get Report Descriptor was successful */
19470Sstevel@tonic-gate 		if (hidparser_parse_report_descriptor(
19480Sstevel@tonic-gate 		    data->b_rptr,
19490Sstevel@tonic-gate 		    hidp->hid_hid_descr.wReportDescriptorLength,
19500Sstevel@tonic-gate 		    &hidp->hid_hid_descr,
19510Sstevel@tonic-gate 		    &hidp->hid_report_descr) == HIDPARSER_SUCCESS) {
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate 			/* find max intr-in xfer length */
19540Sstevel@tonic-gate 			hidparser_find_max_packet_size_from_report_descriptor(
19550Sstevel@tonic-gate 			    hidp->hid_report_descr, &hpack);
19560Sstevel@tonic-gate 			/* round up to the nearest byte */
19570Sstevel@tonic-gate 			hidp->hid_packet_size = (hpack.max_packet_size + 7) / 8;
19580Sstevel@tonic-gate 
19590Sstevel@tonic-gate 			/* if report id is used, add more more byte for it */
19600Sstevel@tonic-gate 			if (hpack.report_id != HID_REPORT_ID_UNDEFINED) {
19610Sstevel@tonic-gate 				hidp->hid_packet_size++;
19620Sstevel@tonic-gate 			}
19630Sstevel@tonic-gate 		} else {
19640Sstevel@tonic-gate 			USB_DPRINTF_L1(PRINT_MASK_ATTA, hidp->hid_log_handle,
19650Sstevel@tonic-gate 			    "Invalid Report Descriptor");
19660Sstevel@tonic-gate 			freemsg(data);
19670Sstevel@tonic-gate 
19680Sstevel@tonic-gate 			return (USB_FAILURE);
19690Sstevel@tonic-gate 		}
19700Sstevel@tonic-gate 
19710Sstevel@tonic-gate 		freemsg(data);
19720Sstevel@tonic-gate 
19730Sstevel@tonic-gate 		return (USB_SUCCESS);
19740Sstevel@tonic-gate 	}
19750Sstevel@tonic-gate }
19760Sstevel@tonic-gate 
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate /*
19790Sstevel@tonic-gate  * hid_set_idle:
19800Sstevel@tonic-gate  *	Make a clas specific request to SET_IDLE.
19810Sstevel@tonic-gate  *	In this case send no reports if state has not changed.
19820Sstevel@tonic-gate  *	See HID 7.2.4.
19830Sstevel@tonic-gate  */
19840Sstevel@tonic-gate /*ARGSUSED*/
19850Sstevel@tonic-gate static void
hid_set_idle(hid_state_t * hidp)19860Sstevel@tonic-gate hid_set_idle(hid_state_t	*hidp)
19870Sstevel@tonic-gate {
19880Sstevel@tonic-gate 	usb_cr_t	completion_reason;
19890Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
19900Sstevel@tonic-gate 	usb_ctrl_setup_t setup = {
19910Sstevel@tonic-gate 	    USB_DEV_REQ_HOST_TO_DEV |	/* bmRequestType */
19926898Sfb209375 	    USB_DEV_REQ_TYPE_CLASS |
19936898Sfb209375 	    USB_DEV_REQ_RCPT_IF,
19940Sstevel@tonic-gate 	    SET_IDLE,			/* bRequest */
19950Sstevel@tonic-gate 	    DURATION,			/* wValue */
19960Sstevel@tonic-gate 	    0,				/* wIndex: interface, fill in later */
19970Sstevel@tonic-gate 	    0,				/* wLength */
19980Sstevel@tonic-gate 	    0				/* attributes */
19996898Sfb209375 	    };
20000Sstevel@tonic-gate 
20010Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
20020Sstevel@tonic-gate 	    "hid_set_idle: Begin");
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 	setup.wIndex = hidp->hid_if_descr.bInterfaceNumber;
20050Sstevel@tonic-gate 	if (usb_pipe_ctrl_xfer_wait(
20060Sstevel@tonic-gate 	    hidp->hid_default_pipe,
20070Sstevel@tonic-gate 	    &setup,
20080Sstevel@tonic-gate 	    NULL,			/* no data to send. */
20090Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
20100Sstevel@tonic-gate 
20110Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
20120Sstevel@tonic-gate 		    "Failed while trying to set idle,"
20130Sstevel@tonic-gate 		    "cr = %d, cb_flags = 0x%x\n",
20140Sstevel@tonic-gate 		    completion_reason, cb_flags);
20150Sstevel@tonic-gate 	}
20160Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
20176898Sfb209375 	    "hid_set_idle: End");
20180Sstevel@tonic-gate }
20190Sstevel@tonic-gate 
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate /*
20220Sstevel@tonic-gate  * hid_set_protocol:
20230Sstevel@tonic-gate  *	Initialize the device to set the preferred protocol
20240Sstevel@tonic-gate  */
20250Sstevel@tonic-gate /*ARGSUSED*/
20260Sstevel@tonic-gate static void
hid_set_protocol(hid_state_t * hidp,int protocol)20270Sstevel@tonic-gate hid_set_protocol(hid_state_t *hidp, int protocol)
20280Sstevel@tonic-gate {
20290Sstevel@tonic-gate 	usb_cr_t	completion_reason;
20300Sstevel@tonic-gate 	usb_cb_flags_t	cb_flags;
20310Sstevel@tonic-gate 	usb_ctrl_setup_t setup;
20320Sstevel@tonic-gate 
20330Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
20340Sstevel@tonic-gate 	    "hid_set_protocol(%d): Begin", protocol);
20350Sstevel@tonic-gate 
20360Sstevel@tonic-gate 	/* initialize the setup request */
20370Sstevel@tonic-gate 	setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
20380Sstevel@tonic-gate 	    USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
20390Sstevel@tonic-gate 	setup.bRequest = SET_PROTOCOL;
20407492SZhigang.Lu@Sun.COM 	setup.wValue = (uint16_t)protocol;
20410Sstevel@tonic-gate 	setup.wIndex = hidp->hid_if_descr.bInterfaceNumber;
20420Sstevel@tonic-gate 	setup.wLength = 0;
20430Sstevel@tonic-gate 	setup.attrs = 0;
20440Sstevel@tonic-gate 	if (usb_pipe_ctrl_xfer_wait(
20450Sstevel@tonic-gate 	    hidp->hid_default_pipe,	/* bmRequestType */
20460Sstevel@tonic-gate 	    &setup,
20470Sstevel@tonic-gate 	    NULL,			/* no data to send */
20480Sstevel@tonic-gate 	    &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
20490Sstevel@tonic-gate 		/*
20500Sstevel@tonic-gate 		 * Some devices fail to follow the specification
20510Sstevel@tonic-gate 		 * and instead of STALLing, they continously
20520Sstevel@tonic-gate 		 * NAK the SET_IDLE command. We need to reset
20530Sstevel@tonic-gate 		 * the pipe then, so that ohci doesn't panic.
20540Sstevel@tonic-gate 		 */
20550Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hidp->hid_log_handle,
20560Sstevel@tonic-gate 		    "Failed while trying to set protocol:%d,"
20570Sstevel@tonic-gate 		    "cr =  %d cb_flags = 0x%x\n",
20580Sstevel@tonic-gate 		    completion_reason, cb_flags, protocol);
20590Sstevel@tonic-gate 	}
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
20626898Sfb209375 	    "hid_set_protocol: End");
20630Sstevel@tonic-gate }
20640Sstevel@tonic-gate 
20650Sstevel@tonic-gate 
20660Sstevel@tonic-gate /*
20670Sstevel@tonic-gate  * hid_detach_cleanup:
20680Sstevel@tonic-gate  *	called by attach and detach for cleanup.
20690Sstevel@tonic-gate  */
20700Sstevel@tonic-gate static void
hid_detach_cleanup(dev_info_t * dip,hid_state_t * hidp)20710Sstevel@tonic-gate hid_detach_cleanup(dev_info_t *dip, hid_state_t *hidp)
20720Sstevel@tonic-gate {
20730Sstevel@tonic-gate 	int	flags = hidp->hid_attach_flags;
20740Sstevel@tonic-gate 	int	rval;
20750Sstevel@tonic-gate 	hid_power_t	*hidpm;
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
20780Sstevel@tonic-gate 	    "hid_detach_cleanup: Begin");
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	if ((hidp->hid_attach_flags & HID_LOCK_INIT) == 0) {
20810Sstevel@tonic-gate 
20820Sstevel@tonic-gate 		goto done;
20830Sstevel@tonic-gate 	}
20840Sstevel@tonic-gate 
20850Sstevel@tonic-gate 	/*
20860Sstevel@tonic-gate 	 * Disable the event callbacks first, after this point, event
20870Sstevel@tonic-gate 	 * callbacks will never get called. Note we shouldn't hold
20880Sstevel@tonic-gate 	 * mutex while unregistering events because there may be a
20890Sstevel@tonic-gate 	 * competing event callback thread. Event callbacks are done
20900Sstevel@tonic-gate 	 * with ndi mutex held and this can cause a potential deadlock.
20910Sstevel@tonic-gate 	 */
20920Sstevel@tonic-gate 	usb_unregister_event_cbs(dip, &hid_events);
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate 	hidpm = hidp->hid_pm;
20970Sstevel@tonic-gate 
20980Sstevel@tonic-gate 	USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
20996898Sfb209375 	    "hid_detach_cleanup: hidpm=0x%p", (void *)hidpm);
21000Sstevel@tonic-gate 
21010Sstevel@tonic-gate 	if (hidpm && (hidp->hid_dev_state != USB_DEV_DISCONNECTED)) {
21020Sstevel@tonic-gate 
21030Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
21040Sstevel@tonic-gate 		hid_pm_busy_component(hidp);
21050Sstevel@tonic-gate 		if (hid_is_pm_enabled(dip) == USB_SUCCESS) {
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate 			if (hidpm->hid_wakeup_enabled) {
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate 				/* First bring the device to full power */
21100Sstevel@tonic-gate 				(void) pm_raise_power(dip, 0,
21110Sstevel@tonic-gate 				    USB_DEV_OS_FULL_PWR);
21120Sstevel@tonic-gate 
21130Sstevel@tonic-gate 				/* Disable remote wakeup */
21140Sstevel@tonic-gate 				rval = usb_handle_remote_wakeup(dip,
21150Sstevel@tonic-gate 				    USB_REMOTE_WAKEUP_DISABLE);
21160Sstevel@tonic-gate 
21170Sstevel@tonic-gate 				if (rval != DDI_SUCCESS) {
21180Sstevel@tonic-gate 					USB_DPRINTF_L2(PRINT_MASK_ALL,
21190Sstevel@tonic-gate 					    hidp->hid_log_handle,
21200Sstevel@tonic-gate 					    "hid_detach_cleanup: "
21210Sstevel@tonic-gate 					    "disble remote wakeup failed, "
21220Sstevel@tonic-gate 					    "rval= %d", rval);
21230Sstevel@tonic-gate 				}
21240Sstevel@tonic-gate 			}
21250Sstevel@tonic-gate 
21260Sstevel@tonic-gate 			(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
21270Sstevel@tonic-gate 		}
21280Sstevel@tonic-gate 		hid_pm_idle_component(hidp);
21290Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
21300Sstevel@tonic-gate 	}
21310Sstevel@tonic-gate 
21320Sstevel@tonic-gate 	if (hidpm) {
21330Sstevel@tonic-gate 		freemsg(hidpm->hid_pm_pwrup);
21340Sstevel@tonic-gate 		kmem_free(hidpm, sizeof (hid_power_t));
21350Sstevel@tonic-gate 		hidp->hid_pm = NULL;
21360Sstevel@tonic-gate 	}
21370Sstevel@tonic-gate 
21380Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
21390Sstevel@tonic-gate 
21400Sstevel@tonic-gate 	if (hidp->hid_report_descr != NULL) {
21410Sstevel@tonic-gate 		(void) hidparser_free_report_descriptor_handle(
21426898Sfb209375 		    hidp->hid_report_descr);
21430Sstevel@tonic-gate 	}
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 	if (flags & HID_MINOR_NODES) {
21460Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
21470Sstevel@tonic-gate 	}
21480Sstevel@tonic-gate 
21490Sstevel@tonic-gate 	mutex_destroy(&hidp->hid_mutex);
21500Sstevel@tonic-gate 
21510Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
21520Sstevel@tonic-gate 	    "hid_detach_cleanup: End");
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate done:
21550Sstevel@tonic-gate 	usb_client_detach(dip, hidp->hid_dev_data);
21560Sstevel@tonic-gate 	usb_free_log_hdl(hidp->hid_log_handle);
21570Sstevel@tonic-gate 	ddi_soft_state_free(hid_statep, hidp->hid_instance);
21580Sstevel@tonic-gate 
21590Sstevel@tonic-gate 	ddi_prop_remove_all(dip);
21600Sstevel@tonic-gate }
21610Sstevel@tonic-gate 
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate /*
21640Sstevel@tonic-gate  * hid_start_intr_polling:
21650Sstevel@tonic-gate  *	Allocate an interrupt request structure, initialize,
21660Sstevel@tonic-gate  *	and start interrupt transfers.
21670Sstevel@tonic-gate  */
21680Sstevel@tonic-gate static int
hid_start_intr_polling(hid_state_t * hidp)21690Sstevel@tonic-gate hid_start_intr_polling(hid_state_t *hidp)
21700Sstevel@tonic-gate {
21710Sstevel@tonic-gate 	usb_intr_req_t	*req;
21720Sstevel@tonic-gate 	int rval = USB_SUCCESS;
21730Sstevel@tonic-gate 
21740Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
21750Sstevel@tonic-gate 	    "hid_start_intr_polling: "
217610153SAaron.Zang@Sun.COM 	    "dev_state=%s internal_str_flag=%d external_str_flag=%d ph=0x%p",
217710153SAaron.Zang@Sun.COM 	    usb_str_dev_state(hidp->hid_dev_state), hidp->hid_internal_flag,
217810153SAaron.Zang@Sun.COM 	    hidp->hid_external_flag, (void *)hidp->hid_interrupt_pipe);
217910153SAaron.Zang@Sun.COM 
218010153SAaron.Zang@Sun.COM 	if (HID_IS_OPEN(hidp) && (hidp->hid_interrupt_pipe != NULL)) {
21810Sstevel@tonic-gate 		/*
21820Sstevel@tonic-gate 		 * initialize interrupt pipe request structure
21830Sstevel@tonic-gate 		 */
21840Sstevel@tonic-gate 		req = usb_alloc_intr_req(hidp->hid_dip, 0, USB_FLAGS_SLEEP);
21850Sstevel@tonic-gate 		req->intr_client_private = (usb_opaque_t)hidp;
21860Sstevel@tonic-gate 		req->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
21876898Sfb209375 		    USB_ATTRS_AUTOCLEARING;
21880Sstevel@tonic-gate 		req->intr_len = hidp->hid_packet_size;
21890Sstevel@tonic-gate 		req->intr_cb = hid_interrupt_pipe_callback;
21900Sstevel@tonic-gate 		req->intr_exc_cb = hid_interrupt_pipe_exception_callback;
21910Sstevel@tonic-gate 
21920Sstevel@tonic-gate 		/*
21930Sstevel@tonic-gate 		 * Start polling on the interrupt pipe.
21940Sstevel@tonic-gate 		 */
21950Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
21960Sstevel@tonic-gate 
21970Sstevel@tonic-gate 		if ((rval = usb_pipe_intr_xfer(hidp->hid_interrupt_pipe, req,
21980Sstevel@tonic-gate 		    USB_FLAGS_SLEEP)) != USB_SUCCESS) {
21990Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_PM, hidp->hid_log_handle,
22000Sstevel@tonic-gate 			    "hid_start_intr_polling failed: rval = %d",
22010Sstevel@tonic-gate 			    rval);
22020Sstevel@tonic-gate 			usb_free_intr_req(req);
22030Sstevel@tonic-gate 		}
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
22060Sstevel@tonic-gate 	}
22070Sstevel@tonic-gate 
22080Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
22090Sstevel@tonic-gate 	    "hid_start_intr_polling: done, rval = %d", rval);
22100Sstevel@tonic-gate 
22110Sstevel@tonic-gate 	return (rval);
22120Sstevel@tonic-gate }
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate 
22150Sstevel@tonic-gate /*
22160Sstevel@tonic-gate  * hid_close_intr_pipe:
22170Sstevel@tonic-gate  *	close the interrupt pipe after draining all callbacks
22180Sstevel@tonic-gate  */
22190Sstevel@tonic-gate static void
hid_close_intr_pipe(hid_state_t * hidp)22200Sstevel@tonic-gate hid_close_intr_pipe(hid_state_t *hidp)
22210Sstevel@tonic-gate {
22220Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle,
22230Sstevel@tonic-gate 	    "hid_close_intr_pipe: Begin");
22240Sstevel@tonic-gate 
22250Sstevel@tonic-gate 	if (hidp->hid_interrupt_pipe) {
22260Sstevel@tonic-gate 		/*
22270Sstevel@tonic-gate 		 * Close the interrupt pipe
22280Sstevel@tonic-gate 		 */
22290Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
22300Sstevel@tonic-gate 		usb_pipe_close(hidp->hid_dip, hidp->hid_interrupt_pipe,
22310Sstevel@tonic-gate 		    USB_FLAGS_SLEEP, NULL, NULL);
22320Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
22330Sstevel@tonic-gate 		hidp->hid_interrupt_pipe = NULL;
22340Sstevel@tonic-gate 	}
22350Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, hidp->hid_log_handle,
22360Sstevel@tonic-gate 	    "hid_close_intr_pipe: End");
22370Sstevel@tonic-gate }
22380Sstevel@tonic-gate 
22390Sstevel@tonic-gate 
22400Sstevel@tonic-gate /*
22410Sstevel@tonic-gate  * hid_mctl_receive:
22420Sstevel@tonic-gate  *	Handle M_CTL messages from upper stream.  If
22430Sstevel@tonic-gate  *	we don't understand the command, free message.
22440Sstevel@tonic-gate  */
22450Sstevel@tonic-gate static int
hid_mctl_receive(register queue_t * q,register mblk_t * mp)22460Sstevel@tonic-gate hid_mctl_receive(register queue_t *q, register mblk_t *mp)
22470Sstevel@tonic-gate {
224810153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)q->q_ptr;
22490Sstevel@tonic-gate 	struct iocblk	*iocp;
22500Sstevel@tonic-gate 	int		error = HID_FAILURE;
22510Sstevel@tonic-gate 	uchar_t		request_type;
22520Sstevel@tonic-gate 	hid_req_t	*hid_req_data = NULL;
22530Sstevel@tonic-gate 	hid_polled_input_callback_t hid_polled_input;
2254918Sqz150045 	hid_vid_pid_t	hid_vid_pid;
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
22570Sstevel@tonic-gate 	    "hid_mctl_receive");
22580Sstevel@tonic-gate 
22590Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
22600Sstevel@tonic-gate 
22610Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
22620Sstevel@tonic-gate 	case HID_SET_REPORT:
22630Sstevel@tonic-gate 		/* FALLTHRU */
22640Sstevel@tonic-gate 	case HID_SET_IDLE:
22650Sstevel@tonic-gate 		/* FALLTHRU */
22660Sstevel@tonic-gate 	case HID_SET_PROTOCOL:
22670Sstevel@tonic-gate 		request_type = USB_DEV_REQ_HOST_TO_DEV |
22680Sstevel@tonic-gate 		    USB_DEV_REQ_RCPT_IF | USB_DEV_REQ_TYPE_CLASS;
22690Sstevel@tonic-gate 
22700Sstevel@tonic-gate 		break;
22710Sstevel@tonic-gate 	case HID_GET_REPORT:
22720Sstevel@tonic-gate 		/* FALLTHRU */
22730Sstevel@tonic-gate 	case HID_GET_IDLE:
22740Sstevel@tonic-gate 		/* FALLTHRU */
22750Sstevel@tonic-gate 	case HID_GET_PROTOCOL:
22760Sstevel@tonic-gate 		request_type = USB_DEV_REQ_DEV_TO_HOST |
22770Sstevel@tonic-gate 		    USB_DEV_REQ_RCPT_IF | USB_DEV_REQ_TYPE_CLASS;
22780Sstevel@tonic-gate 
22790Sstevel@tonic-gate 		break;
22800Sstevel@tonic-gate 	case HID_GET_PARSER_HANDLE:
22810Sstevel@tonic-gate 		if (canputnext(RD(q))) {
22820Sstevel@tonic-gate 			freemsg(mp->b_cont);
22830Sstevel@tonic-gate 			mp->b_cont = hid_data2mblk(
22840Sstevel@tonic-gate 			    (uchar_t *)&hidp->hid_report_descr,
22850Sstevel@tonic-gate 			    sizeof (hidp->hid_report_descr));
22860Sstevel@tonic-gate 			if (mp->b_cont == NULL) {
22870Sstevel@tonic-gate 				/*
22880Sstevel@tonic-gate 				 * can't allocate mblk, indicate
22890Sstevel@tonic-gate 				 * that nothing is returned
22900Sstevel@tonic-gate 				 */
22910Sstevel@tonic-gate 				iocp->ioc_count = 0;
22920Sstevel@tonic-gate 			} else {
22930Sstevel@tonic-gate 				iocp->ioc_count =
22940Sstevel@tonic-gate 				    sizeof (hidp->hid_report_descr);
22950Sstevel@tonic-gate 			}
22960Sstevel@tonic-gate 			qreply(q, mp);
22970Sstevel@tonic-gate 
22980Sstevel@tonic-gate 			return (HID_SUCCESS);
22990Sstevel@tonic-gate 		} else {
23000Sstevel@tonic-gate 
23010Sstevel@tonic-gate 			/* retry */
23020Sstevel@tonic-gate 			return (HID_ENQUEUE);
23030Sstevel@tonic-gate 		}
2304918Sqz150045 	case HID_GET_VID_PID:
2305918Sqz150045 		if (canputnext(RD(q))) {
2306918Sqz150045 			freemsg(mp->b_cont);
2307918Sqz150045 
2308918Sqz150045 			hid_vid_pid.VendorId =
23096898Sfb209375 			    hidp->hid_dev_descr->idVendor;
2310918Sqz150045 			hid_vid_pid.ProductId =
23116898Sfb209375 			    hidp->hid_dev_descr->idProduct;
2312918Sqz150045 
2313918Sqz150045 			mp->b_cont = hid_data2mblk(
2314918Sqz150045 			    (uchar_t *)&hid_vid_pid, sizeof (hid_vid_pid_t));
2315918Sqz150045 			if (mp->b_cont == NULL) {
2316918Sqz150045 				/*
2317918Sqz150045 				 * can't allocate mblk, indicate that nothing
2318918Sqz150045 				 * is being returned.
2319918Sqz150045 				 */
2320918Sqz150045 				iocp->ioc_count = 0;
2321918Sqz150045 			} else {
2322918Sqz150045 				iocp->ioc_count =
2323918Sqz150045 				    sizeof (hid_vid_pid_t);
2324918Sqz150045 			}
2325918Sqz150045 			qreply(q, mp);
2326918Sqz150045 
2327918Sqz150045 			return (HID_SUCCESS);
2328918Sqz150045 		} else {
2329918Sqz150045 
2330918Sqz150045 			/* retry */
2331918Sqz150045 			return (HID_ENQUEUE);
2332918Sqz150045 		}
23330Sstevel@tonic-gate 	case HID_OPEN_POLLED_INPUT:
23340Sstevel@tonic-gate 		if (canputnext(RD(q))) {
23350Sstevel@tonic-gate 			freemsg(mp->b_cont);
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate 			/* Initialize the structure */
23380Sstevel@tonic-gate 			hid_polled_input.hid_polled_version =
23396898Sfb209375 			    HID_POLLED_INPUT_V0;
23400Sstevel@tonic-gate 			hid_polled_input.hid_polled_read = hid_polled_read;
23410Sstevel@tonic-gate 			hid_polled_input.hid_polled_input_enter =
23426898Sfb209375 			    hid_polled_input_enter;
23430Sstevel@tonic-gate 			hid_polled_input.hid_polled_input_exit =
23446898Sfb209375 			    hid_polled_input_exit;
23450Sstevel@tonic-gate 			hid_polled_input.hid_polled_input_handle =
23466898Sfb209375 			    (hid_polled_handle_t)hidp;
23470Sstevel@tonic-gate 
23480Sstevel@tonic-gate 			mp->b_cont = hid_data2mblk(
23490Sstevel@tonic-gate 			    (uchar_t *)&hid_polled_input,
23500Sstevel@tonic-gate 			    sizeof (hid_polled_input_callback_t));
23510Sstevel@tonic-gate 			if (mp->b_cont == NULL) {
23520Sstevel@tonic-gate 				/*
23530Sstevel@tonic-gate 				 * can't allocate mblk, indicate that nothing
23540Sstevel@tonic-gate 				 * is being returned.
23550Sstevel@tonic-gate 				 */
23560Sstevel@tonic-gate 				iocp->ioc_count = 0;
23570Sstevel@tonic-gate 			} else {
23580Sstevel@tonic-gate 				/* Call down into USBA */
23590Sstevel@tonic-gate 				(void) hid_polled_input_init(hidp);
23600Sstevel@tonic-gate 
23610Sstevel@tonic-gate 				iocp->ioc_count =
23620Sstevel@tonic-gate 				    sizeof (hid_polled_input_callback_t);
23630Sstevel@tonic-gate 			}
23640Sstevel@tonic-gate 			qreply(q, mp);
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate 			return (HID_SUCCESS);
23670Sstevel@tonic-gate 		} else {
23680Sstevel@tonic-gate 
23690Sstevel@tonic-gate 			/* retry */
23700Sstevel@tonic-gate 			return (HID_ENQUEUE);
23710Sstevel@tonic-gate 		}
23720Sstevel@tonic-gate 	case HID_CLOSE_POLLED_INPUT:
23730Sstevel@tonic-gate 		/* Call down into USBA */
23740Sstevel@tonic-gate 		(void) hid_polled_input_fini(hidp);
23750Sstevel@tonic-gate 
23760Sstevel@tonic-gate 		iocp->ioc_count = 0;
23770Sstevel@tonic-gate 		qreply(q, mp);
23780Sstevel@tonic-gate 
23790Sstevel@tonic-gate 		return (HID_SUCCESS);
23800Sstevel@tonic-gate 	default:
23810Sstevel@tonic-gate 		hid_qreply_merror(q, mp, EINVAL);
23820Sstevel@tonic-gate 
23830Sstevel@tonic-gate 		return (HID_FAILURE);
23840Sstevel@tonic-gate 	}
23850Sstevel@tonic-gate 
23860Sstevel@tonic-gate 	/*
23870Sstevel@tonic-gate 	 * These (device executable) commands require a hid_req_t.
23880Sstevel@tonic-gate 	 * Make sure one is present
23890Sstevel@tonic-gate 	 */
23900Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
23910Sstevel@tonic-gate 		hid_qreply_merror(q, mp, EINVAL);
23920Sstevel@tonic-gate 
23930Sstevel@tonic-gate 		return (error);
23940Sstevel@tonic-gate 	} else {
23950Sstevel@tonic-gate 		hid_req_data = (hid_req_t *)mp->b_cont->b_rptr;
23960Sstevel@tonic-gate 		if ((iocp->ioc_cmd == HID_SET_REPORT) &&
23972278Sgc161489 		    (hid_req_data->hid_req_wLength == 0)) {
23980Sstevel@tonic-gate 			hid_qreply_merror(q, mp, EINVAL);
23990Sstevel@tonic-gate 
24000Sstevel@tonic-gate 			return (error);
24010Sstevel@tonic-gate 		}
24020Sstevel@tonic-gate 	}
24030Sstevel@tonic-gate 
24040Sstevel@tonic-gate 	/*
24050Sstevel@tonic-gate 	 * Check is version no. is correct. This
24060Sstevel@tonic-gate 	 * is coming from the user
24070Sstevel@tonic-gate 	 */
24080Sstevel@tonic-gate 	if (hid_req_data->hid_req_version_no != HID_VERSION_V_0) {
24090Sstevel@tonic-gate 		hid_qreply_merror(q, mp, EINVAL);
24100Sstevel@tonic-gate 
24110Sstevel@tonic-gate 		return (error);
24120Sstevel@tonic-gate 	}
24130Sstevel@tonic-gate 
24140Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
24150Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
24160Sstevel@tonic-gate 	    "hid_mctl_receive: dev_state=%s",
24170Sstevel@tonic-gate 	    usb_str_dev_state(hidp->hid_dev_state));
24180Sstevel@tonic-gate 
24190Sstevel@tonic-gate 	switch (hidp->hid_dev_state) {
24200Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
24210Sstevel@tonic-gate 		/*
24220Sstevel@tonic-gate 		 * get the device full powered. We get a callback
24230Sstevel@tonic-gate 		 * which enables the WQ and kicks off IO
24240Sstevel@tonic-gate 		 */
24250Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_HID_POWER_CHANGE;
24260Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
24270Sstevel@tonic-gate 		if (usb_req_raise_power(hidp->hid_dip, 0,
24280Sstevel@tonic-gate 		    USB_DEV_OS_FULL_PWR, hid_power_change_callback,
24290Sstevel@tonic-gate 		    hidp, 0) != USB_SUCCESS) {
24300Sstevel@tonic-gate 			/* we retry raising power in wsrv */
24310Sstevel@tonic-gate 			mutex_enter(&hidp->hid_mutex);
24320Sstevel@tonic-gate 			hidp->hid_dev_state = USB_DEV_PWRED_DOWN;
24330Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
24340Sstevel@tonic-gate 		}
24350Sstevel@tonic-gate 		error = HID_ENQUEUE;
24360Sstevel@tonic-gate 
24370Sstevel@tonic-gate 		break;
24380Sstevel@tonic-gate 	case USB_DEV_HID_POWER_CHANGE:
24390Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
24400Sstevel@tonic-gate 		error = HID_ENQUEUE;
24410Sstevel@tonic-gate 
24420Sstevel@tonic-gate 		break;
24430Sstevel@tonic-gate 	case USB_DEV_ONLINE:
244410153SAaron.Zang@Sun.COM 		if (HID_STREAMS_FLAG(q, hidp) != HID_STREAMS_DISMANTLING) {
24450Sstevel@tonic-gate 			/* Send a message down */
24460Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
24479432SPengcheng.Chen@Sun.COM 			error = hid_mctl_execute_cmd(q, request_type,
24480Sstevel@tonic-gate 			    hid_req_data, mp);
24490Sstevel@tonic-gate 			if (error == HID_FAILURE) {
24500Sstevel@tonic-gate 				hid_qreply_merror(q, mp, EIO);
24510Sstevel@tonic-gate 			}
24520Sstevel@tonic-gate 		} else {
24530Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
24540Sstevel@tonic-gate 			hid_qreply_merror(q, mp, EIO);
24550Sstevel@tonic-gate 		}
24560Sstevel@tonic-gate 
24570Sstevel@tonic-gate 		break;
24580Sstevel@tonic-gate 	default:
24590Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
24600Sstevel@tonic-gate 		hid_qreply_merror(q, mp, EIO);
24610Sstevel@tonic-gate 
24620Sstevel@tonic-gate 		break;
24630Sstevel@tonic-gate 	}
24640Sstevel@tonic-gate 
24650Sstevel@tonic-gate 	return (error);
24660Sstevel@tonic-gate }
24670Sstevel@tonic-gate 
24680Sstevel@tonic-gate 
24690Sstevel@tonic-gate /*
24700Sstevel@tonic-gate  * hid_mctl_execute_cmd:
24710Sstevel@tonic-gate  *	Send the command to the device.
24720Sstevel@tonic-gate  */
24730Sstevel@tonic-gate static int
hid_mctl_execute_cmd(queue_t * q,int request_type,hid_req_t * hid_req_data,mblk_t * mp)24749432SPengcheng.Chen@Sun.COM hid_mctl_execute_cmd(queue_t *q, int request_type, hid_req_t *hid_req_data,
24759432SPengcheng.Chen@Sun.COM     mblk_t *mp)
24760Sstevel@tonic-gate {
24770Sstevel@tonic-gate 	int		request_index;
24780Sstevel@tonic-gate 	struct iocblk	*iocp;
24790Sstevel@tonic-gate 	hid_default_pipe_arg_t	*def_pipe_arg;
248010153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)q->q_ptr;
24810Sstevel@tonic-gate 
24820Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
24830Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
24846898Sfb209375 	    "hid_mctl_execute_cmd: iocp=0x%p", (void *)iocp);
24850Sstevel@tonic-gate 
24860Sstevel@tonic-gate 	request_index = hidp->hid_if_descr.bInterfaceNumber;
24870Sstevel@tonic-gate 
24880Sstevel@tonic-gate 	/*
24890Sstevel@tonic-gate 	 * Set up the argument to be passed back to hid
24900Sstevel@tonic-gate 	 * when the asynchronous control callback is
24910Sstevel@tonic-gate 	 * executed.
24920Sstevel@tonic-gate 	 */
24930Sstevel@tonic-gate 	def_pipe_arg = kmem_zalloc(sizeof (hid_default_pipe_arg_t), 0);
24940Sstevel@tonic-gate 
24950Sstevel@tonic-gate 	if (def_pipe_arg == NULL) {
24960Sstevel@tonic-gate 
24970Sstevel@tonic-gate 		return (HID_FAILURE);
24980Sstevel@tonic-gate 	}
24990Sstevel@tonic-gate 
25009432SPengcheng.Chen@Sun.COM 	def_pipe_arg->hid_default_pipe_arg_queue = q;
25010Sstevel@tonic-gate 	def_pipe_arg->hid_default_pipe_arg_mctlmsg.ioc_cmd = iocp->ioc_cmd;
25020Sstevel@tonic-gate 	def_pipe_arg->hid_default_pipe_arg_mctlmsg.ioc_count = 0;
25030Sstevel@tonic-gate 	def_pipe_arg->hid_default_pipe_arg_mblk = mp;
25040Sstevel@tonic-gate 
25050Sstevel@tonic-gate 	/*
25060Sstevel@tonic-gate 	 * Send the command down to USBA through default
25070Sstevel@tonic-gate 	 * pipe.
25080Sstevel@tonic-gate 	 */
25099432SPengcheng.Chen@Sun.COM 	if (hid_send_async_ctrl_request(def_pipe_arg, hid_req_data,
25109432SPengcheng.Chen@Sun.COM 	    request_type, iocp->ioc_cmd, request_index) != USB_SUCCESS) {
25110Sstevel@tonic-gate 
25120Sstevel@tonic-gate 		kmem_free(def_pipe_arg, sizeof (hid_default_pipe_arg_t));
25130Sstevel@tonic-gate 
25140Sstevel@tonic-gate 		return (HID_FAILURE);
25150Sstevel@tonic-gate 	}
25160Sstevel@tonic-gate 
25170Sstevel@tonic-gate 	return (HID_INPROGRESS);
25180Sstevel@tonic-gate }
25190Sstevel@tonic-gate 
25200Sstevel@tonic-gate 
25210Sstevel@tonic-gate /*
25220Sstevel@tonic-gate  * hid_send_async_ctrl_request:
25230Sstevel@tonic-gate  *	Send an asynchronous control request to USBA.  Since hid is a STREAMS
25240Sstevel@tonic-gate  *	driver, it is not allowed to wait in its entry points except for the
25250Sstevel@tonic-gate  *	open and close entry points.  Therefore, hid must use the asynchronous
25260Sstevel@tonic-gate  *	USBA calls.
25270Sstevel@tonic-gate  */
25280Sstevel@tonic-gate static int
hid_send_async_ctrl_request(hid_default_pipe_arg_t * hid_default_pipe_arg,hid_req_t * hid_request,uchar_t request_type,int request_request,ushort_t request_index)25299432SPengcheng.Chen@Sun.COM hid_send_async_ctrl_request(hid_default_pipe_arg_t *hid_default_pipe_arg,
25309432SPengcheng.Chen@Sun.COM 			hid_req_t *hid_request,
25310Sstevel@tonic-gate 			uchar_t request_type, int request_request,
25329432SPengcheng.Chen@Sun.COM 			ushort_t request_index)
25330Sstevel@tonic-gate {
25349432SPengcheng.Chen@Sun.COM 	queue_t		*q = hid_default_pipe_arg->hid_default_pipe_arg_queue;
253510153SAaron.Zang@Sun.COM 	hid_state_t	*hidp = (hid_state_t *)q->q_ptr;
25360Sstevel@tonic-gate 	usb_ctrl_req_t	*ctrl_req;
25370Sstevel@tonic-gate 	int		rval;
25380Sstevel@tonic-gate 	size_t		length = 0;
25390Sstevel@tonic-gate 
25400Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
25410Sstevel@tonic-gate 	    "hid_send_async_ctrl_request: "
25420Sstevel@tonic-gate 	    "rq_type=%d rq_rq=%d index=%d",
25430Sstevel@tonic-gate 	    request_type, request_request, request_index);
25440Sstevel@tonic-gate 
25450Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
25460Sstevel@tonic-gate 	hidp->hid_default_pipe_req++;
25470Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
25480Sstevel@tonic-gate 
25490Sstevel@tonic-gate 	/*
25500Sstevel@tonic-gate 	 * Note that ctrl_req->ctrl_data should be allocated by usba
25510Sstevel@tonic-gate 	 * only for IN requests. OUT request(e.g SET_REPORT) can have a
25520Sstevel@tonic-gate 	 * non-zero wLength value but ctrl_data would be allocated by
25530Sstevel@tonic-gate 	 * client for them.
25540Sstevel@tonic-gate 	 */
25552278Sgc161489 	if (hid_request->hid_req_wLength >= MAX_REPORT_DATA) {
25562278Sgc161489 		USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
25572278Sgc161489 		    "hid_req_wLength is exceeded");
25582278Sgc161489 		return (USB_FAILURE);
25592278Sgc161489 	}
25602278Sgc161489 	if ((request_type & USB_DEV_REQ_DIR_MASK) == USB_DEV_REQ_DEV_TO_HOST) {
25612278Sgc161489 		length = hid_request->hid_req_wLength;
25620Sstevel@tonic-gate 	}
25630Sstevel@tonic-gate 
25640Sstevel@tonic-gate 	if ((ctrl_req = usb_alloc_ctrl_req(hidp->hid_dip, length, 0)) == NULL) {
25650Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
25660Sstevel@tonic-gate 		    "unable to alloc ctrl req. async trans failed");
25670Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
25680Sstevel@tonic-gate 		hidp->hid_default_pipe_req--;
25690Sstevel@tonic-gate 		ASSERT(hidp->hid_default_pipe_req >= 0);
25700Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
25710Sstevel@tonic-gate 
25720Sstevel@tonic-gate 		return (USB_FAILURE);
25730Sstevel@tonic-gate 	}
25740Sstevel@tonic-gate 
25752278Sgc161489 	if ((request_type & USB_DEV_REQ_DIR_MASK) == USB_DEV_REQ_HOST_TO_DEV) {
25760Sstevel@tonic-gate 		ASSERT((length == 0) && (ctrl_req->ctrl_data == NULL));
25770Sstevel@tonic-gate 	}
25780Sstevel@tonic-gate 
25790Sstevel@tonic-gate 	ctrl_req->ctrl_bmRequestType	= request_type;
25800Sstevel@tonic-gate 	ctrl_req->ctrl_bRequest 	= (uint8_t)request_request;
25810Sstevel@tonic-gate 	ctrl_req->ctrl_wValue		= hid_request->hid_req_wValue;
25820Sstevel@tonic-gate 	ctrl_req->ctrl_wIndex		= request_index;
25830Sstevel@tonic-gate 	ctrl_req->ctrl_wLength		= hid_request->hid_req_wLength;
25842278Sgc161489 	/* host to device: create a msg from hid_req_data */
25852278Sgc161489 	if ((request_type & USB_DEV_REQ_DIR_MASK) == USB_DEV_REQ_HOST_TO_DEV) {
25862278Sgc161489 		mblk_t *pblk = allocb(hid_request->hid_req_wLength, BPRI_HI);
25872278Sgc161489 		if (pblk == NULL) {
25882278Sgc161489 			usb_free_ctrl_req(ctrl_req);
25892278Sgc161489 			return (USB_FAILURE);
25902278Sgc161489 		}
25912278Sgc161489 		bcopy(hid_request->hid_req_data, pblk->b_wptr,
25926898Sfb209375 		    hid_request->hid_req_wLength);
25932278Sgc161489 		pblk->b_wptr += hid_request->hid_req_wLength;
25942278Sgc161489 		ctrl_req->ctrl_data = pblk;
25952278Sgc161489 	}
25960Sstevel@tonic-gate 	ctrl_req->ctrl_attributes	= USB_ATTRS_AUTOCLEARING;
25970Sstevel@tonic-gate 	ctrl_req->ctrl_client_private	= (usb_opaque_t)hid_default_pipe_arg;
25980Sstevel@tonic-gate 	ctrl_req->ctrl_cb		= hid_default_pipe_callback;
25990Sstevel@tonic-gate 	ctrl_req->ctrl_exc_cb		= hid_default_pipe_exception_callback;
26000Sstevel@tonic-gate 
26010Sstevel@tonic-gate 	if ((rval = usb_pipe_ctrl_xfer(hidp->hid_default_pipe,
26020Sstevel@tonic-gate 	    ctrl_req, 0)) != USB_SUCCESS) {
26030Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
26040Sstevel@tonic-gate 		hidp->hid_default_pipe_req--;
26050Sstevel@tonic-gate 		ASSERT(hidp->hid_default_pipe_req >= 0);
26060Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
26070Sstevel@tonic-gate 
26080Sstevel@tonic-gate 		usb_free_ctrl_req(ctrl_req);
26090Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ALL, hidp->hid_log_handle,
26100Sstevel@tonic-gate 		    "usb_pipe_ctrl_xfer() failed. rval = %d", rval);
26110Sstevel@tonic-gate 
26120Sstevel@tonic-gate 		return (USB_FAILURE);
26130Sstevel@tonic-gate 	}
26140Sstevel@tonic-gate 
26150Sstevel@tonic-gate 	return (USB_SUCCESS);
26160Sstevel@tonic-gate }
26170Sstevel@tonic-gate 
26180Sstevel@tonic-gate /*
26190Sstevel@tonic-gate  * hid_create_pm_components:
26200Sstevel@tonic-gate  *	Create the pm components required for power management.
26210Sstevel@tonic-gate  *	For keyboard/mouse, the components is created only if the device
26220Sstevel@tonic-gate  *	supports a remote wakeup.
26230Sstevel@tonic-gate  *	For other hid devices they are created unconditionally.
26240Sstevel@tonic-gate  */
26250Sstevel@tonic-gate static void
hid_create_pm_components(dev_info_t * dip,hid_state_t * hidp)26260Sstevel@tonic-gate hid_create_pm_components(dev_info_t *dip, hid_state_t *hidp)
26270Sstevel@tonic-gate {
26280Sstevel@tonic-gate 	hid_power_t	*hidpm;
26290Sstevel@tonic-gate 	uint_t		pwr_states;
26300Sstevel@tonic-gate 
26310Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
26320Sstevel@tonic-gate 	    "hid_create_pm_components: Begin");
26330Sstevel@tonic-gate 
26340Sstevel@tonic-gate 	/* Allocate the state structure */
26350Sstevel@tonic-gate 	hidpm = kmem_zalloc(sizeof (hid_power_t), KM_SLEEP);
26360Sstevel@tonic-gate 	hidp->hid_pm = hidpm;
26370Sstevel@tonic-gate 	hidpm->hid_state = hidp;
26380Sstevel@tonic-gate 	hidpm->hid_raise_power = B_FALSE;
26390Sstevel@tonic-gate 	hidpm->hid_pm_capabilities = 0;
26400Sstevel@tonic-gate 	hidpm->hid_current_power = USB_DEV_OS_FULL_PWR;
26410Sstevel@tonic-gate 
26420Sstevel@tonic-gate 	switch (hidp->hid_if_descr.bInterfaceProtocol) {
26430Sstevel@tonic-gate 	case KEYBOARD_PROTOCOL:
26440Sstevel@tonic-gate 	case MOUSE_PROTOCOL:
26450Sstevel@tonic-gate 		hidpm->hid_pm_strategy = HID_PM_ACTIVITY;
26460Sstevel@tonic-gate 		if ((hid_is_pm_enabled(dip) == USB_SUCCESS) &&
26470Sstevel@tonic-gate 		    (usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) ==
26480Sstevel@tonic-gate 		    USB_SUCCESS)) {
26490Sstevel@tonic-gate 
26500Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_PM, hidp->hid_log_handle,
26510Sstevel@tonic-gate 			    "hid_create_pm_components: Remote Wakeup Enabled");
26520Sstevel@tonic-gate 
26530Sstevel@tonic-gate 			if (usb_create_pm_components(dip, &pwr_states) ==
26540Sstevel@tonic-gate 			    USB_SUCCESS) {
26550Sstevel@tonic-gate 				hidpm->hid_wakeup_enabled = 1;
26560Sstevel@tonic-gate 				hidpm->hid_pwr_states = (uint8_t)pwr_states;
26570Sstevel@tonic-gate 			}
26580Sstevel@tonic-gate 		}
26590Sstevel@tonic-gate 
26600Sstevel@tonic-gate 		break;
26610Sstevel@tonic-gate 	default:
26620Sstevel@tonic-gate 		hidpm->hid_pm_strategy = HID_PM_OPEN_CLOSE;
26630Sstevel@tonic-gate 		if ((hid_is_pm_enabled(dip) == USB_SUCCESS) &&
26640Sstevel@tonic-gate 		    (usb_create_pm_components(dip, &pwr_states) ==
26650Sstevel@tonic-gate 		    USB_SUCCESS)) {
26660Sstevel@tonic-gate 			hidpm->hid_wakeup_enabled = 0;
26670Sstevel@tonic-gate 			hidpm->hid_pwr_states = (uint8_t)pwr_states;
26680Sstevel@tonic-gate 		}
26690Sstevel@tonic-gate 
26700Sstevel@tonic-gate 		break;
26710Sstevel@tonic-gate 	}
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_PM, hidp->hid_log_handle,
26746898Sfb209375 	    "hid_create_pm_components: END");
26750Sstevel@tonic-gate }
26760Sstevel@tonic-gate 
26770Sstevel@tonic-gate 
26780Sstevel@tonic-gate /*
26790Sstevel@tonic-gate  * hid_is_pm_enabled
26800Sstevel@tonic-gate  *	Check if the device is pm enabled. Always enable
26810Sstevel@tonic-gate  *	pm on the new SUN mouse
26820Sstevel@tonic-gate  */
26830Sstevel@tonic-gate static int
hid_is_pm_enabled(dev_info_t * dip)26840Sstevel@tonic-gate hid_is_pm_enabled(dev_info_t *dip)
26850Sstevel@tonic-gate {
26860Sstevel@tonic-gate 	hid_state_t	*hidp = ddi_get_soft_state(hid_statep,
26876898Sfb209375 	    ddi_get_instance(dip));
26880Sstevel@tonic-gate 
26890Sstevel@tonic-gate 	if (strcmp(ddi_node_name(dip), "mouse") == 0) {
2690880Sfrits 		/* check for overrides first */
2691880Sfrits 		if (hid_pm_mouse ||
2692880Sfrits 		    (ddi_prop_exists(DDI_DEV_T_ANY, dip,
2693880Sfrits 		    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
2694880Sfrits 		    "hid-mouse-pm-enable") == 1)) {
2695880Sfrits 
2696880Sfrits 			return (USB_SUCCESS);
2697880Sfrits 		}
2698880Sfrits 
26990Sstevel@tonic-gate 		/*
2700880Sfrits 		 * Always enable PM for 1.05 or greater SUN mouse
27010Sstevel@tonic-gate 		 * hidp->hid_dev_descr won't be NULL.
27020Sstevel@tonic-gate 		 */
2703880Sfrits 		if ((hidp->hid_dev_descr->idVendor ==
27040Sstevel@tonic-gate 		    HID_SUN_MOUSE_VENDOR_ID) &&
27050Sstevel@tonic-gate 		    (hidp->hid_dev_descr->idProduct ==
27060Sstevel@tonic-gate 		    HID_SUN_MOUSE_PROD_ID) &&
27070Sstevel@tonic-gate 		    (hidp->hid_dev_descr->bcdDevice >=
2708880Sfrits 		    HID_SUN_MOUSE_BCDDEVICE)) {
27090Sstevel@tonic-gate 
27100Sstevel@tonic-gate 			return (USB_SUCCESS);
27110Sstevel@tonic-gate 		}
27120Sstevel@tonic-gate 	} else {
27130Sstevel@tonic-gate 
27140Sstevel@tonic-gate 		return (USB_SUCCESS);
27150Sstevel@tonic-gate 	}
27160Sstevel@tonic-gate 
27170Sstevel@tonic-gate 	return (USB_FAILURE);
27180Sstevel@tonic-gate }
27190Sstevel@tonic-gate 
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate /*
27220Sstevel@tonic-gate  * hid_save_device_state
27230Sstevel@tonic-gate  *	Save the current device/driver state.
27240Sstevel@tonic-gate  */
27250Sstevel@tonic-gate static void
hid_save_device_state(hid_state_t * hidp)27260Sstevel@tonic-gate hid_save_device_state(hid_state_t *hidp)
27270Sstevel@tonic-gate {
27280Sstevel@tonic-gate 	struct iocblk	*mctlmsg;
27290Sstevel@tonic-gate 	mblk_t		*mp;
27300Sstevel@tonic-gate 	queue_t		*q;
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, hidp->hid_log_handle,
27330Sstevel@tonic-gate 	    "hid_save_device_state");
27340Sstevel@tonic-gate 
273510153SAaron.Zang@Sun.COM 	if (!(HID_IS_OPEN(hidp)))
273610153SAaron.Zang@Sun.COM 		return;
273710153SAaron.Zang@Sun.COM 
273810153SAaron.Zang@Sun.COM 	if (hidp->hid_internal_flag == HID_STREAMS_OPEN) {
27390Sstevel@tonic-gate 		/*
27409432SPengcheng.Chen@Sun.COM 		 * Send MCTLs up indicating that the device
27419432SPengcheng.Chen@Sun.COM 		 * will loose its state
27420Sstevel@tonic-gate 		 */
274310153SAaron.Zang@Sun.COM 		q = hidp->hid_internal_rq;
274410153SAaron.Zang@Sun.COM 
274510153SAaron.Zang@Sun.COM 		mutex_exit(&hidp->hid_mutex);
274610153SAaron.Zang@Sun.COM 		if (canputnext(q)) {
274710153SAaron.Zang@Sun.COM 			mp = allocb(sizeof (struct iocblk), BPRI_HI);
274810153SAaron.Zang@Sun.COM 			if (mp != NULL) {
274910153SAaron.Zang@Sun.COM 				mp->b_datap->db_type = M_CTL;
275010153SAaron.Zang@Sun.COM 				mctlmsg = (struct iocblk *)
275110153SAaron.Zang@Sun.COM 				    mp->b_datap->db_base;
275210153SAaron.Zang@Sun.COM 				mctlmsg->ioc_cmd = HID_DISCONNECT_EVENT;
275310153SAaron.Zang@Sun.COM 				mctlmsg->ioc_count = 0;
275410153SAaron.Zang@Sun.COM 				putnext(q, mp);
27559432SPengcheng.Chen@Sun.COM 			}
27569432SPengcheng.Chen@Sun.COM 		}
27570Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
27580Sstevel@tonic-gate 	}
275910153SAaron.Zang@Sun.COM 
276010153SAaron.Zang@Sun.COM 	if (hidp->hid_external_flag == HID_STREAMS_OPEN) {
276110153SAaron.Zang@Sun.COM 		/*
276210153SAaron.Zang@Sun.COM 		 * Send MCTLs up indicating that the device
276310153SAaron.Zang@Sun.COM 		 * will loose its state
276410153SAaron.Zang@Sun.COM 		 */
276510153SAaron.Zang@Sun.COM 		q = hidp->hid_external_rq;
276610153SAaron.Zang@Sun.COM 
276710153SAaron.Zang@Sun.COM 		mutex_exit(&hidp->hid_mutex);
276810153SAaron.Zang@Sun.COM 		if (canputnext(q)) {
276910153SAaron.Zang@Sun.COM 			mp = allocb(sizeof (struct iocblk), BPRI_HI);
277010153SAaron.Zang@Sun.COM 			if (mp != NULL) {
277110153SAaron.Zang@Sun.COM 				mp->b_datap->db_type = M_CTL;
277210153SAaron.Zang@Sun.COM 				mctlmsg = (struct iocblk *)
277310153SAaron.Zang@Sun.COM 				    mp->b_datap->db_base;
277410153SAaron.Zang@Sun.COM 				mctlmsg->ioc_cmd = HID_DISCONNECT_EVENT;
277510153SAaron.Zang@Sun.COM 				mctlmsg->ioc_count = 0;
277610153SAaron.Zang@Sun.COM 				putnext(q, mp);
277710153SAaron.Zang@Sun.COM 			}
277810153SAaron.Zang@Sun.COM 		}
277910153SAaron.Zang@Sun.COM 		mutex_enter(&hidp->hid_mutex);
278010153SAaron.Zang@Sun.COM 	}
278110153SAaron.Zang@Sun.COM 
278210153SAaron.Zang@Sun.COM 	mutex_exit(&hidp->hid_mutex);
278310153SAaron.Zang@Sun.COM 	/* stop polling on the intr pipe */
278410153SAaron.Zang@Sun.COM 	usb_pipe_stop_intr_polling(hidp->hid_interrupt_pipe, USB_FLAGS_SLEEP);
278510153SAaron.Zang@Sun.COM 	mutex_enter(&hidp->hid_mutex);
27860Sstevel@tonic-gate }
27870Sstevel@tonic-gate 
27880Sstevel@tonic-gate 
27890Sstevel@tonic-gate /*
27900Sstevel@tonic-gate  * hid_restore_device_state:
27910Sstevel@tonic-gate  *	Set original configuration of the device.
27920Sstevel@tonic-gate  *	Reopen intr pipe.
27930Sstevel@tonic-gate  *	Enable wrq - this starts new transactions on the control pipe.
27940Sstevel@tonic-gate  */
27950Sstevel@tonic-gate static void
hid_restore_device_state(dev_info_t * dip,hid_state_t * hidp)27960Sstevel@tonic-gate hid_restore_device_state(dev_info_t *dip, hid_state_t *hidp)
27970Sstevel@tonic-gate {
27980Sstevel@tonic-gate 	int		rval;
27990Sstevel@tonic-gate 	hid_power_t	*hidpm;
28000Sstevel@tonic-gate 	struct iocblk	*mctlmsg;
28010Sstevel@tonic-gate 	mblk_t		*mp;
280210153SAaron.Zang@Sun.COM 	queue_t		*q;
28030Sstevel@tonic-gate 
28040Sstevel@tonic-gate 	hid_pm_busy_component(hidp);
28050Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
28060Sstevel@tonic-gate 
28070Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hidp->hid_log_handle,
28080Sstevel@tonic-gate 	    "hid_restore_device_state: %s",
28090Sstevel@tonic-gate 	    usb_str_dev_state(hidp->hid_dev_state));
28100Sstevel@tonic-gate 
28110Sstevel@tonic-gate 	hidpm = hidp->hid_pm;
28120Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 	/* First bring the device to full power */
28150Sstevel@tonic-gate 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
28160Sstevel@tonic-gate 
28170Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
28180Sstevel@tonic-gate 	if (hidp->hid_dev_state == USB_DEV_ONLINE) {
28190Sstevel@tonic-gate 		/*
28200Sstevel@tonic-gate 		 * We failed the checkpoint, there is no need to restore
28210Sstevel@tonic-gate 		 * the device state
28220Sstevel@tonic-gate 		 */
28230Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
28240Sstevel@tonic-gate 		hid_pm_idle_component(hidp);
28250Sstevel@tonic-gate 
28260Sstevel@tonic-gate 		return;
28270Sstevel@tonic-gate 	}
28280Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
28290Sstevel@tonic-gate 
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	/* Check if we are talking to the same device */
28320Sstevel@tonic-gate 	if (usb_check_same_device(dip, hidp->hid_log_handle, USB_LOG_L2,
28330Sstevel@tonic-gate 	    PRINT_MASK_ALL, USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS) {
28340Sstevel@tonic-gate 
28350Sstevel@tonic-gate 		/* change the device state from suspended to disconnected */
28360Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
28370Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_DISCONNECTED;
28380Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
28390Sstevel@tonic-gate 		hid_pm_idle_component(hidp);
28409432SPengcheng.Chen@Sun.COM 		goto nodev;
28410Sstevel@tonic-gate 	}
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 	hid_set_idle(hidp);
28440Sstevel@tonic-gate 	hid_set_protocol(hidp, SET_REPORT_PROTOCOL);
28450Sstevel@tonic-gate 
28460Sstevel@tonic-gate 	mutex_enter(&hidp->hid_mutex);
28470Sstevel@tonic-gate 	/* if the device had remote wakeup earlier, enable it again */
28480Sstevel@tonic-gate 	if (hidpm->hid_wakeup_enabled) {
28490Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
28500Sstevel@tonic-gate 
28510Sstevel@tonic-gate 		if ((rval = usb_handle_remote_wakeup(hidp->hid_dip,
28520Sstevel@tonic-gate 		    USB_REMOTE_WAKEUP_ENABLE)) != USB_SUCCESS) {
28530Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ATTA,
28540Sstevel@tonic-gate 			    hidp->hid_log_handle,
28550Sstevel@tonic-gate 			    "usb_handle_remote_wakeup failed (%d)", rval);
28560Sstevel@tonic-gate 		}
28570Sstevel@tonic-gate 
28580Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
28590Sstevel@tonic-gate 	}
28600Sstevel@tonic-gate 
28610Sstevel@tonic-gate 	/*
28620Sstevel@tonic-gate 	 * restart polling on the interrupt pipe only if the device
28630Sstevel@tonic-gate 	 * was previously operational (open)
28640Sstevel@tonic-gate 	 */
286510153SAaron.Zang@Sun.COM 	if (HID_IS_OPEN(hidp)) {
28660Sstevel@tonic-gate 		if ((rval = hid_start_intr_polling(hidp)) != USB_SUCCESS) {
28670Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ATTA, hidp->hid_log_handle,
28680Sstevel@tonic-gate 			    "hid_restore_device_state:"
28690Sstevel@tonic-gate 			    "unable to restart intr pipe poll"
28700Sstevel@tonic-gate 			    " rval = %d ", rval);
28710Sstevel@tonic-gate 			/*
28720Sstevel@tonic-gate 			 * change the device state from
28730Sstevel@tonic-gate 			 * suspended to disconnected
28740Sstevel@tonic-gate 			 */
28750Sstevel@tonic-gate 			hidp->hid_dev_state = USB_DEV_DISCONNECTED;
28760Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
28770Sstevel@tonic-gate 			hid_pm_idle_component(hidp);
28789432SPengcheng.Chen@Sun.COM 			goto nodev;
28790Sstevel@tonic-gate 		}
28800Sstevel@tonic-gate 
28810Sstevel@tonic-gate 		if (hidp->hid_dev_state == USB_DEV_DISCONNECTED) {
28820Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
28830Sstevel@tonic-gate 			    "device is being re-connected");
28840Sstevel@tonic-gate 		}
28850Sstevel@tonic-gate 
28860Sstevel@tonic-gate 		/* set the device state ONLINE */
28870Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_ONLINE;
28880Sstevel@tonic-gate 
28890Sstevel@tonic-gate 		/* inform upstream modules that the device is back */
289010153SAaron.Zang@Sun.COM 		if (hidp->hid_internal_flag == HID_STREAMS_OPEN) {
289110153SAaron.Zang@Sun.COM 			q = hidp->hid_internal_rq;
28929432SPengcheng.Chen@Sun.COM 
28939432SPengcheng.Chen@Sun.COM 			mutex_exit(&hidp->hid_mutex);
289410153SAaron.Zang@Sun.COM 			if (canputnext(q)) {
28959432SPengcheng.Chen@Sun.COM 				mp = allocb(sizeof (struct iocblk), BPRI_HI);
28969432SPengcheng.Chen@Sun.COM 				if (mp != NULL) {
28979432SPengcheng.Chen@Sun.COM 					mp->b_datap->db_type = M_CTL;
28989432SPengcheng.Chen@Sun.COM 					mctlmsg = (struct iocblk *)
28999432SPengcheng.Chen@Sun.COM 					    mp->b_datap->db_base;
29009432SPengcheng.Chen@Sun.COM 					mctlmsg->ioc_cmd = HID_CONNECT_EVENT;
29019432SPengcheng.Chen@Sun.COM 					mctlmsg->ioc_count = 0;
290210153SAaron.Zang@Sun.COM 					putnext(q, mp);
29039432SPengcheng.Chen@Sun.COM 				}
29040Sstevel@tonic-gate 			}
29059432SPengcheng.Chen@Sun.COM 			/* enable write side q */
290610153SAaron.Zang@Sun.COM 			qenable(WR(q));
29079432SPengcheng.Chen@Sun.COM 			mutex_enter(&hidp->hid_mutex);
290810153SAaron.Zang@Sun.COM 		}
290910153SAaron.Zang@Sun.COM 
291010153SAaron.Zang@Sun.COM 		if (hidp->hid_external_flag == HID_STREAMS_OPEN) {
291110153SAaron.Zang@Sun.COM 			q = hidp->hid_external_rq;
291210153SAaron.Zang@Sun.COM 
291310153SAaron.Zang@Sun.COM 			mutex_exit(&hidp->hid_mutex);
291410153SAaron.Zang@Sun.COM 			if (canputnext(q)) {
291510153SAaron.Zang@Sun.COM 				mp = allocb(sizeof (struct iocblk), BPRI_HI);
291610153SAaron.Zang@Sun.COM 				if (mp != NULL) {
291710153SAaron.Zang@Sun.COM 					mp->b_datap->db_type = M_CTL;
291810153SAaron.Zang@Sun.COM 					mctlmsg = (struct iocblk *)
291910153SAaron.Zang@Sun.COM 					    mp->b_datap->db_base;
292010153SAaron.Zang@Sun.COM 					mctlmsg->ioc_cmd = HID_CONNECT_EVENT;
292110153SAaron.Zang@Sun.COM 					mctlmsg->ioc_count = 0;
292210153SAaron.Zang@Sun.COM 					putnext(q, mp);
292310153SAaron.Zang@Sun.COM 				}
292410153SAaron.Zang@Sun.COM 			}
292510153SAaron.Zang@Sun.COM 			/* enable write side q */
292610153SAaron.Zang@Sun.COM 			qenable(WR(q));
292710153SAaron.Zang@Sun.COM 			mutex_enter(&hidp->hid_mutex);
29280Sstevel@tonic-gate 		}
29290Sstevel@tonic-gate 	} else {
29300Sstevel@tonic-gate 		/* set the device state ONLINE */
29310Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_ONLINE;
29320Sstevel@tonic-gate 	}
29330Sstevel@tonic-gate 
29340Sstevel@tonic-gate 	mutex_exit(&hidp->hid_mutex);
29350Sstevel@tonic-gate 	hid_pm_idle_component(hidp);
29369432SPengcheng.Chen@Sun.COM 	return;
29379432SPengcheng.Chen@Sun.COM 
29389432SPengcheng.Chen@Sun.COM nodev:
29399432SPengcheng.Chen@Sun.COM 	/*
29409432SPengcheng.Chen@Sun.COM 	 * Notify applications about device removal. This only
29419432SPengcheng.Chen@Sun.COM 	 * applies to an external (aka. physical) open. Not sure how to
29429432SPengcheng.Chen@Sun.COM 	 * notify consconfig to close the internal minor node.
29439432SPengcheng.Chen@Sun.COM 	 */
29449432SPengcheng.Chen@Sun.COM 	mutex_enter(&hidp->hid_mutex);
294510153SAaron.Zang@Sun.COM 
294610153SAaron.Zang@Sun.COM 	if ((q = hidp->hid_external_rq) == NULL) {
294710153SAaron.Zang@Sun.COM 		mutex_exit(&hidp->hid_mutex);
294810153SAaron.Zang@Sun.COM 		return;
29499432SPengcheng.Chen@Sun.COM 	}
295010153SAaron.Zang@Sun.COM 
29519432SPengcheng.Chen@Sun.COM 	mutex_exit(&hidp->hid_mutex);
295210153SAaron.Zang@Sun.COM 	mp = allocb(sizeof (uchar_t), BPRI_HI);
295310153SAaron.Zang@Sun.COM 	if (mp != NULL) {
295410153SAaron.Zang@Sun.COM 		mp->b_datap->db_type = M_ERROR;
295510153SAaron.Zang@Sun.COM 		mp->b_rptr = mp->b_datap->db_base;
295610153SAaron.Zang@Sun.COM 		mp->b_wptr = mp->b_rptr + sizeof (char);
295710153SAaron.Zang@Sun.COM 		*mp->b_rptr = ENODEV;
295810153SAaron.Zang@Sun.COM 		putnext(q, mp);
295910153SAaron.Zang@Sun.COM 	}
29600Sstevel@tonic-gate }
29610Sstevel@tonic-gate 
29620Sstevel@tonic-gate 
29630Sstevel@tonic-gate /*
29640Sstevel@tonic-gate  * hid_qreply_merror:
29650Sstevel@tonic-gate  *	Pass an error message up.
29660Sstevel@tonic-gate  */
29670Sstevel@tonic-gate static void
hid_qreply_merror(queue_t * q,mblk_t * mp,uchar_t errval)29680Sstevel@tonic-gate hid_qreply_merror(queue_t *q, mblk_t *mp, uchar_t errval)
29690Sstevel@tonic-gate {
29700Sstevel@tonic-gate 	mp->b_datap->db_type = M_ERROR;
29710Sstevel@tonic-gate 	if (mp->b_cont) {
29720Sstevel@tonic-gate 		freemsg(mp->b_cont);
29730Sstevel@tonic-gate 		mp->b_cont = NULL;
29740Sstevel@tonic-gate 	}
29750Sstevel@tonic-gate 	mp->b_rptr = mp->b_datap->db_base;
29760Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + sizeof (char);
29770Sstevel@tonic-gate 	*mp->b_rptr = errval;
29780Sstevel@tonic-gate 
29790Sstevel@tonic-gate 	qreply(q, mp);
29800Sstevel@tonic-gate }
29810Sstevel@tonic-gate 
29820Sstevel@tonic-gate 
29830Sstevel@tonic-gate /*
29840Sstevel@tonic-gate  * hid_data2mblk:
29850Sstevel@tonic-gate  *	Form an mblk from the given data
29860Sstevel@tonic-gate  */
29870Sstevel@tonic-gate static mblk_t *
hid_data2mblk(uchar_t * buf,int len)29880Sstevel@tonic-gate hid_data2mblk(uchar_t *buf, int len)
29890Sstevel@tonic-gate {
29900Sstevel@tonic-gate 	mblk_t	*mp = NULL;
29910Sstevel@tonic-gate 
29920Sstevel@tonic-gate 	if (len >= 0) {
29930Sstevel@tonic-gate 		mp = allocb(len, BPRI_HI);
29940Sstevel@tonic-gate 		if (mp) {
29950Sstevel@tonic-gate 			bcopy(buf, mp->b_datap->db_base, len);
29960Sstevel@tonic-gate 			mp->b_wptr += len;
29970Sstevel@tonic-gate 		}
29980Sstevel@tonic-gate 	}
29990Sstevel@tonic-gate 
30000Sstevel@tonic-gate 	return (mp);
30010Sstevel@tonic-gate }
30020Sstevel@tonic-gate 
30030Sstevel@tonic-gate 
30040Sstevel@tonic-gate /*
30050Sstevel@tonic-gate  * hid_flush :
30060Sstevel@tonic-gate  *	Flush data already sent upstreams to client module.
30070Sstevel@tonic-gate  */
30080Sstevel@tonic-gate static void
hid_flush(queue_t * q)30090Sstevel@tonic-gate hid_flush(queue_t *q)
30100Sstevel@tonic-gate {
30110Sstevel@tonic-gate 	/*
30120Sstevel@tonic-gate 	 * Flush pending data already sent upstream
30130Sstevel@tonic-gate 	 */
30140Sstevel@tonic-gate 	if ((q != NULL) && (q->q_next != NULL)) {
30150Sstevel@tonic-gate 		(void) putnextctl1(q, M_FLUSH, FLUSHR);
30160Sstevel@tonic-gate 	}
30170Sstevel@tonic-gate }
30180Sstevel@tonic-gate 
30190Sstevel@tonic-gate 
30200Sstevel@tonic-gate static void
hid_pm_busy_component(hid_state_t * hid_statep)30210Sstevel@tonic-gate hid_pm_busy_component(hid_state_t *hid_statep)
30220Sstevel@tonic-gate {
30230Sstevel@tonic-gate 	ASSERT(!mutex_owned(&hid_statep->hid_mutex));
30240Sstevel@tonic-gate 
30250Sstevel@tonic-gate 	if (hid_statep->hid_pm != NULL) {
30260Sstevel@tonic-gate 		mutex_enter(&hid_statep->hid_mutex);
30270Sstevel@tonic-gate 		hid_statep->hid_pm->hid_pm_busy++;
30280Sstevel@tonic-gate 
30290Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_PM, hid_statep->hid_log_handle,
30300Sstevel@tonic-gate 		    "hid_pm_busy_component: %d",
30310Sstevel@tonic-gate 		    hid_statep->hid_pm->hid_pm_busy);
30320Sstevel@tonic-gate 
30330Sstevel@tonic-gate 		mutex_exit(&hid_statep->hid_mutex);
30340Sstevel@tonic-gate 		if (pm_busy_component(hid_statep->hid_dip, 0) != DDI_SUCCESS) {
30350Sstevel@tonic-gate 			mutex_enter(&hid_statep->hid_mutex);
30360Sstevel@tonic-gate 			hid_statep->hid_pm->hid_pm_busy--;
30370Sstevel@tonic-gate 
30380Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_PM,
30390Sstevel@tonic-gate 			    hid_statep->hid_log_handle,
30400Sstevel@tonic-gate 			    "hid_pm_busy_component failed: %d",
30410Sstevel@tonic-gate 			    hid_statep->hid_pm->hid_pm_busy);
30420Sstevel@tonic-gate 
30430Sstevel@tonic-gate 			mutex_exit(&hid_statep->hid_mutex);
30440Sstevel@tonic-gate 		}
30450Sstevel@tonic-gate 
30460Sstevel@tonic-gate 	}
30470Sstevel@tonic-gate }
30480Sstevel@tonic-gate 
30490Sstevel@tonic-gate 
30500Sstevel@tonic-gate static void
hid_pm_idle_component(hid_state_t * hid_statep)30510Sstevel@tonic-gate hid_pm_idle_component(hid_state_t *hid_statep)
30520Sstevel@tonic-gate {
30530Sstevel@tonic-gate 	ASSERT(!mutex_owned(&hid_statep->hid_mutex));
30540Sstevel@tonic-gate 
30550Sstevel@tonic-gate 	if (hid_statep->hid_pm != NULL) {
30560Sstevel@tonic-gate 		if (pm_idle_component(hid_statep->hid_dip, 0) == DDI_SUCCESS) {
30570Sstevel@tonic-gate 			mutex_enter(&hid_statep->hid_mutex);
30580Sstevel@tonic-gate 			ASSERT(hid_statep->hid_pm->hid_pm_busy > 0);
30590Sstevel@tonic-gate 			hid_statep->hid_pm->hid_pm_busy--;
30600Sstevel@tonic-gate 
30610Sstevel@tonic-gate 			USB_DPRINTF_L4(PRINT_MASK_PM,
30620Sstevel@tonic-gate 			    hid_statep->hid_log_handle,
30630Sstevel@tonic-gate 			    "hid_pm_idle_component: %d",
30640Sstevel@tonic-gate 			    hid_statep->hid_pm->hid_pm_busy);
30650Sstevel@tonic-gate 
30660Sstevel@tonic-gate 			mutex_exit(&hid_statep->hid_mutex);
30670Sstevel@tonic-gate 		}
30680Sstevel@tonic-gate 	}
30690Sstevel@tonic-gate }
30700Sstevel@tonic-gate 
30710Sstevel@tonic-gate 
30720Sstevel@tonic-gate /*
30730Sstevel@tonic-gate  * hid_pwrlvl0:
30740Sstevel@tonic-gate  *	Functions to handle power transition for various levels
30750Sstevel@tonic-gate  *	These functions act as place holders to issue USB commands
30760Sstevel@tonic-gate  *	to the devices to change their power levels
30770Sstevel@tonic-gate  */
30780Sstevel@tonic-gate static int
hid_pwrlvl0(hid_state_t * hidp)30790Sstevel@tonic-gate hid_pwrlvl0(hid_state_t *hidp)
30800Sstevel@tonic-gate {
30810Sstevel@tonic-gate 	hid_power_t	*hidpm;
30820Sstevel@tonic-gate 	int		rval;
30830Sstevel@tonic-gate 	struct iocblk	*mctlmsg;
30840Sstevel@tonic-gate 	mblk_t		*mp_lowpwr, *mp_fullpwr;
30850Sstevel@tonic-gate 	queue_t		*q;
30860Sstevel@tonic-gate 
30870Sstevel@tonic-gate 	hidpm = hidp->hid_pm;
30880Sstevel@tonic-gate 
30890Sstevel@tonic-gate 	switch (hidp->hid_dev_state) {
30900Sstevel@tonic-gate 	case USB_DEV_ONLINE:
30910Sstevel@tonic-gate 		/* Deny the powerdown request if the device is busy */
30920Sstevel@tonic-gate 		if (hidpm->hid_pm_busy != 0) {
30930Sstevel@tonic-gate 
30940Sstevel@tonic-gate 			return (USB_FAILURE);
30950Sstevel@tonic-gate 		}
30960Sstevel@tonic-gate 
309710153SAaron.Zang@Sun.COM 		if (HID_IS_OPEN(hidp)) {
309810153SAaron.Zang@Sun.COM 			q = hidp->hid_inuse_rq;
30990Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
31000Sstevel@tonic-gate 			if (canputnext(q)) {
31010Sstevel@tonic-gate 				/* try to preallocate mblks */
31020Sstevel@tonic-gate 				mp_lowpwr = allocb(
31030Sstevel@tonic-gate 				    (int)sizeof (struct iocblk), BPRI_HI);
31040Sstevel@tonic-gate 				mp_fullpwr = allocb(
31050Sstevel@tonic-gate 				    (int)sizeof (struct iocblk), BPRI_HI);
31060Sstevel@tonic-gate 				if ((mp_lowpwr != NULL) &&
31070Sstevel@tonic-gate 				    (mp_fullpwr != NULL)) {
31080Sstevel@tonic-gate 					/* stop polling */
31090Sstevel@tonic-gate 					usb_pipe_stop_intr_polling(
31100Sstevel@tonic-gate 					    hidp->hid_interrupt_pipe,
31110Sstevel@tonic-gate 					    USB_FLAGS_SLEEP);
31120Sstevel@tonic-gate 
31130Sstevel@tonic-gate 					/*
31140Sstevel@tonic-gate 					 * Send an MCTL up indicating that
31150Sstevel@tonic-gate 					 * we are powering off
31160Sstevel@tonic-gate 					 */
31170Sstevel@tonic-gate 					mp_lowpwr->b_datap->db_type = M_CTL;
31180Sstevel@tonic-gate 					mctlmsg = (struct iocblk *)
31190Sstevel@tonic-gate 					    mp_lowpwr->b_datap->db_base;
31200Sstevel@tonic-gate 					mctlmsg->ioc_cmd = HID_POWER_OFF;
31210Sstevel@tonic-gate 					mctlmsg->ioc_count = 0;
31220Sstevel@tonic-gate 					putnext(q, mp_lowpwr);
31230Sstevel@tonic-gate 
31240Sstevel@tonic-gate 					/* save the full powr mblk */
31250Sstevel@tonic-gate 					mutex_enter(&hidp->hid_mutex);
31260Sstevel@tonic-gate 					hidpm->hid_pm_pwrup = mp_fullpwr;
31270Sstevel@tonic-gate 				} else {
31280Sstevel@tonic-gate 					/*
31290Sstevel@tonic-gate 					 * Since we failed to allocate one
31300Sstevel@tonic-gate 					 * or more mblks, we fail attempt
31310Sstevel@tonic-gate 					 * to go into low power this time
31320Sstevel@tonic-gate 					 */
31330Sstevel@tonic-gate 					freemsg(mp_lowpwr);
31340Sstevel@tonic-gate 					freemsg(mp_fullpwr);
31350Sstevel@tonic-gate 					mutex_enter(&hidp->hid_mutex);
31360Sstevel@tonic-gate 
31370Sstevel@tonic-gate 					return (USB_FAILURE);
31380Sstevel@tonic-gate 				}
31390Sstevel@tonic-gate 			} else {
31400Sstevel@tonic-gate 				/*
31410Sstevel@tonic-gate 				 * Since we can't send an mblk up,
31420Sstevel@tonic-gate 				 * we fail this attempt to go to low power
31430Sstevel@tonic-gate 				 */
31440Sstevel@tonic-gate 				mutex_enter(&hidp->hid_mutex);
31450Sstevel@tonic-gate 
31460Sstevel@tonic-gate 				return (USB_FAILURE);
31470Sstevel@tonic-gate 			}
31480Sstevel@tonic-gate 		}
314910153SAaron.Zang@Sun.COM 
31500Sstevel@tonic-gate 		mutex_exit(&hidp->hid_mutex);
31510Sstevel@tonic-gate 		/* Issue USB D3 command to the device here */
31520Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl3(hidp->hid_dip);
31530Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
31540Sstevel@tonic-gate 
31550Sstevel@tonic-gate 		mutex_enter(&hidp->hid_mutex);
31560Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_PWRED_DOWN;
31570Sstevel@tonic-gate 		hidpm->hid_current_power = USB_DEV_OS_PWR_OFF;
31580Sstevel@tonic-gate 
31590Sstevel@tonic-gate 		/* FALLTHRU */
31600Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
31610Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
31620Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
31630Sstevel@tonic-gate 	default:
31640Sstevel@tonic-gate 		break;
31650Sstevel@tonic-gate 	}
31660Sstevel@tonic-gate 
31670Sstevel@tonic-gate 	return (USB_SUCCESS);
31680Sstevel@tonic-gate }
31690Sstevel@tonic-gate 
31700Sstevel@tonic-gate 
31710Sstevel@tonic-gate /* ARGSUSED */
31720Sstevel@tonic-gate static int
hid_pwrlvl1(hid_state_t * hidp)31730Sstevel@tonic-gate hid_pwrlvl1(hid_state_t *hidp)
31740Sstevel@tonic-gate {
31750Sstevel@tonic-gate 	int		rval;
31760Sstevel@tonic-gate 
31770Sstevel@tonic-gate 	/* Issue USB D2 command to the device here */
31780Sstevel@tonic-gate 	rval = usb_set_device_pwrlvl2(hidp->hid_dip);
31790Sstevel@tonic-gate 	ASSERT(rval == USB_SUCCESS);
31800Sstevel@tonic-gate 
31810Sstevel@tonic-gate 	return (USB_FAILURE);
31820Sstevel@tonic-gate }
31830Sstevel@tonic-gate 
31840Sstevel@tonic-gate 
31850Sstevel@tonic-gate /* ARGSUSED */
31860Sstevel@tonic-gate static int
hid_pwrlvl2(hid_state_t * hidp)31870Sstevel@tonic-gate hid_pwrlvl2(hid_state_t *hidp)
31880Sstevel@tonic-gate {
31890Sstevel@tonic-gate 	int		rval;
31900Sstevel@tonic-gate 
31910Sstevel@tonic-gate 	rval = usb_set_device_pwrlvl1(hidp->hid_dip);
31920Sstevel@tonic-gate 	ASSERT(rval == USB_SUCCESS);
31930Sstevel@tonic-gate 
31940Sstevel@tonic-gate 	return (USB_FAILURE);
31950Sstevel@tonic-gate }
31960Sstevel@tonic-gate 
31970Sstevel@tonic-gate 
31980Sstevel@tonic-gate static int
hid_pwrlvl3(hid_state_t * hidp)31990Sstevel@tonic-gate hid_pwrlvl3(hid_state_t *hidp)
32000Sstevel@tonic-gate {
32010Sstevel@tonic-gate 	hid_power_t	*hidpm;
32020Sstevel@tonic-gate 	int		rval;
32030Sstevel@tonic-gate 	struct iocblk	*mctlmsg;
32040Sstevel@tonic-gate 	mblk_t		*mp;
32050Sstevel@tonic-gate 	queue_t		*q;
32060Sstevel@tonic-gate 
32070Sstevel@tonic-gate 	hidpm = hidp->hid_pm;
32080Sstevel@tonic-gate 
32090Sstevel@tonic-gate 	switch (hidp->hid_dev_state) {
32100Sstevel@tonic-gate 	case USB_DEV_HID_POWER_CHANGE:
32110Sstevel@tonic-gate 	case USB_DEV_PWRED_DOWN:
32120Sstevel@tonic-gate 		/* Issue USB D0 command to the device here */
32130Sstevel@tonic-gate 		rval = usb_set_device_pwrlvl0(hidp->hid_dip);
32140Sstevel@tonic-gate 		ASSERT(rval == USB_SUCCESS);
32150Sstevel@tonic-gate 
321610153SAaron.Zang@Sun.COM 		if (HID_IS_OPEN(hidp)) {
32170Sstevel@tonic-gate 			/* restart polling on intr pipe */
32180Sstevel@tonic-gate 			rval = hid_start_intr_polling(hidp);
32190Sstevel@tonic-gate 			if (rval != USB_SUCCESS) {
32200Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_EVENTS,
32210Sstevel@tonic-gate 				    hidp->hid_log_handle,
32220Sstevel@tonic-gate 				    "unable to restart intr polling rval = %d",
32230Sstevel@tonic-gate 				    rval);
32240Sstevel@tonic-gate 
32250Sstevel@tonic-gate 				return (USB_FAILURE);
32260Sstevel@tonic-gate 			}
32270Sstevel@tonic-gate 
32280Sstevel@tonic-gate 			/* Send an MCTL up indicating device in full  power */
322910153SAaron.Zang@Sun.COM 			q = hidp->hid_inuse_rq;
32300Sstevel@tonic-gate 			mp = hidpm->hid_pm_pwrup;
32310Sstevel@tonic-gate 			hidpm->hid_pm_pwrup = NULL;
32320Sstevel@tonic-gate 			mutex_exit(&hidp->hid_mutex);
32330Sstevel@tonic-gate 			if (canputnext(q)) {
32340Sstevel@tonic-gate 				mp->b_datap->db_type = M_CTL;
323510153SAaron.Zang@Sun.COM 				mctlmsg = (struct iocblk *)
323610153SAaron.Zang@Sun.COM 				    mp->b_datap->db_base;
32370Sstevel@tonic-gate 				mctlmsg->ioc_cmd = HID_FULL_POWER;
32380Sstevel@tonic-gate 				mctlmsg->ioc_count = 0;
32390Sstevel@tonic-gate 				putnext(q, mp);
32400Sstevel@tonic-gate 			} else {
32410Sstevel@tonic-gate 				freemsg(mp);
32420Sstevel@tonic-gate 			}
32430Sstevel@tonic-gate 			mutex_enter(&hidp->hid_mutex);
32440Sstevel@tonic-gate 		}
324510153SAaron.Zang@Sun.COM 
32460Sstevel@tonic-gate 		hidp->hid_dev_state = USB_DEV_ONLINE;
32470Sstevel@tonic-gate 		hidpm->hid_current_power = USB_DEV_OS_FULL_PWR;
32480Sstevel@tonic-gate 
32490Sstevel@tonic-gate 		/* FALLTHRU */
32500Sstevel@tonic-gate 	case USB_DEV_DISCONNECTED:
32510Sstevel@tonic-gate 	case USB_DEV_SUSPENDED:
32520Sstevel@tonic-gate 	case USB_DEV_ONLINE:
32530Sstevel@tonic-gate 
32540Sstevel@tonic-gate 		return (USB_SUCCESS);
32550Sstevel@tonic-gate 	default:
32560Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, hidp->hid_log_handle,
32570Sstevel@tonic-gate 		    "hid_pwrlvl3: Improper State");
32580Sstevel@tonic-gate 
32590Sstevel@tonic-gate 		return (USB_FAILURE);
32600Sstevel@tonic-gate 	}
32610Sstevel@tonic-gate }
32620Sstevel@tonic-gate 
32630Sstevel@tonic-gate 
32640Sstevel@tonic-gate /*
32650Sstevel@tonic-gate  * hid_polled_input_init :
32660Sstevel@tonic-gate  *	This routine calls down to the lower layers to initialize any state
32670Sstevel@tonic-gate  *	information.  This routine initializes the lower layers for input.
32680Sstevel@tonic-gate  */
32690Sstevel@tonic-gate static int
hid_polled_input_init(hid_state_t * hidp)32700Sstevel@tonic-gate hid_polled_input_init(hid_state_t *hidp)
32710Sstevel@tonic-gate {
32720Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
32730Sstevel@tonic-gate 	    "hid_polled_input_init");
32740Sstevel@tonic-gate 
32750Sstevel@tonic-gate 	/*
32760Sstevel@tonic-gate 	 * Call the lower layers to intialize any state information
32770Sstevel@tonic-gate 	 * that they will need to provide the polled characters.
32780Sstevel@tonic-gate 	 */
32790Sstevel@tonic-gate 	if (usb_console_input_init(hidp->hid_dip, hidp->hid_interrupt_pipe,
32800Sstevel@tonic-gate 	    &hidp->hid_polled_raw_buf,
32810Sstevel@tonic-gate 	    &hidp->hid_polled_console_info) != USB_SUCCESS) {
32820Sstevel@tonic-gate 		/*
32830Sstevel@tonic-gate 		 * If for some reason the lower layers cannot initialized, then
32840Sstevel@tonic-gate 		 * bail.
32850Sstevel@tonic-gate 		 */
32860Sstevel@tonic-gate 		(void) hid_polled_input_fini(hidp);
32870Sstevel@tonic-gate 
32880Sstevel@tonic-gate 		return (USB_FAILURE);
32890Sstevel@tonic-gate 	}
32900Sstevel@tonic-gate 
32910Sstevel@tonic-gate 	return (USB_SUCCESS);
32920Sstevel@tonic-gate }
32930Sstevel@tonic-gate 
32940Sstevel@tonic-gate 
32950Sstevel@tonic-gate /*
32960Sstevel@tonic-gate  * hid_polled_input_fini:
32970Sstevel@tonic-gate  *	This routine is called when we are done using this device as an input
32980Sstevel@tonic-gate  *	device.
32990Sstevel@tonic-gate  */
33000Sstevel@tonic-gate static int
hid_polled_input_fini(hid_state_t * hidp)33010Sstevel@tonic-gate hid_polled_input_fini(hid_state_t *hidp)
33020Sstevel@tonic-gate {
33030Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ALL, hidp->hid_log_handle,
33040Sstevel@tonic-gate 	    "hid_polled_input_fini");
33050Sstevel@tonic-gate 
33060Sstevel@tonic-gate 	/*
33070Sstevel@tonic-gate 	 * Call the lower layers to free any state information
33080Sstevel@tonic-gate 	 * only if polled input has been initialised.
33090Sstevel@tonic-gate 	 */
33100Sstevel@tonic-gate 	if ((hidp->hid_polled_console_info) &&
33110Sstevel@tonic-gate 	    (usb_console_input_fini(hidp->hid_polled_console_info) !=
33120Sstevel@tonic-gate 	    USB_SUCCESS)) {
33130Sstevel@tonic-gate 
33140Sstevel@tonic-gate 		return (USB_FAILURE);
33150Sstevel@tonic-gate 	}
33160Sstevel@tonic-gate 	hidp->hid_polled_console_info = NULL;
33170Sstevel@tonic-gate 
33180Sstevel@tonic-gate 	return (USB_SUCCESS);
33190Sstevel@tonic-gate }
33200Sstevel@tonic-gate 
33210Sstevel@tonic-gate 
33220Sstevel@tonic-gate /*
33230Sstevel@tonic-gate  * hid_polled_input_enter:
33240Sstevel@tonic-gate  *	This is the routine that is called in polled mode to save the USB
33250Sstevel@tonic-gate  *	state information before using the USB keyboard as an input device.
33260Sstevel@tonic-gate  *	This routine, and all of the routines that it calls, are responsible
33270Sstevel@tonic-gate  *	for saving any state information so that it can be restored when
33280Sstevel@tonic-gate  *	polling mode is over.
33290Sstevel@tonic-gate  */
33300Sstevel@tonic-gate static int
33310Sstevel@tonic-gate /* ARGSUSED */
hid_polled_input_enter(hid_polled_handle_t hid_polled_inputp)33320Sstevel@tonic-gate hid_polled_input_enter(hid_polled_handle_t hid_polled_inputp)
33330Sstevel@tonic-gate {
33340Sstevel@tonic-gate 	hid_state_t *hidp = (hid_state_t *)hid_polled_inputp;
33350Sstevel@tonic-gate 
33360Sstevel@tonic-gate 	/*
33370Sstevel@tonic-gate 	 * Call the lower layers to tell them to save any state information.
33380Sstevel@tonic-gate 	 */
33390Sstevel@tonic-gate 	(void) usb_console_input_enter(hidp->hid_polled_console_info);
33400Sstevel@tonic-gate 
33410Sstevel@tonic-gate 	return (USB_SUCCESS);
33420Sstevel@tonic-gate }
33430Sstevel@tonic-gate 
33440Sstevel@tonic-gate 
33450Sstevel@tonic-gate /*
33460Sstevel@tonic-gate  * hid_polled_read :
33470Sstevel@tonic-gate  *	This is the routine that is called in polled mode when it wants to read
33480Sstevel@tonic-gate  *	a character.  We will call to the lower layers to see if there is any
33490Sstevel@tonic-gate  *	input data available.  If there is USB scancodes available, we will
33500Sstevel@tonic-gate  *	give them back.
33510Sstevel@tonic-gate  */
33520Sstevel@tonic-gate static int
hid_polled_read(hid_polled_handle_t hid_polled_input,uchar_t ** buffer)33530Sstevel@tonic-gate hid_polled_read(hid_polled_handle_t hid_polled_input, uchar_t **buffer)
33540Sstevel@tonic-gate {
33550Sstevel@tonic-gate 	hid_state_t *hidp = (hid_state_t *)hid_polled_input;
33560Sstevel@tonic-gate 	uint_t			num_bytes;
33570Sstevel@tonic-gate 
33580Sstevel@tonic-gate 	/*
33590Sstevel@tonic-gate 	 * Call the lower layers to get the character from the controller.
33600Sstevel@tonic-gate 	 * The lower layers will return the number of characters that
33610Sstevel@tonic-gate 	 * were put in the raw buffer.	The address of the raw buffer
33620Sstevel@tonic-gate 	 * was passed down to the lower layers during hid_polled_init.
33630Sstevel@tonic-gate 	 */
33640Sstevel@tonic-gate 	if (usb_console_read(hidp->hid_polled_console_info,
33650Sstevel@tonic-gate 	    &num_bytes) != USB_SUCCESS) {
33660Sstevel@tonic-gate 
33670Sstevel@tonic-gate 		return (0);
33680Sstevel@tonic-gate 	}
33690Sstevel@tonic-gate 
33700Sstevel@tonic-gate 	_NOTE(NO_COMPETING_THREADS_NOW);
33710Sstevel@tonic-gate 
33720Sstevel@tonic-gate 	*buffer = hidp->hid_polled_raw_buf;
33730Sstevel@tonic-gate 
33740Sstevel@tonic-gate 	_NOTE(COMPETING_THREADS_NOW);
33750Sstevel@tonic-gate 
33760Sstevel@tonic-gate 	/*
33770Sstevel@tonic-gate 	 * Return the number of characters that were copied into the
33780Sstevel@tonic-gate 	 * polled buffer.
33790Sstevel@tonic-gate 	 */
33800Sstevel@tonic-gate 	return (num_bytes);
33810Sstevel@tonic-gate }
33820Sstevel@tonic-gate 
33830Sstevel@tonic-gate 
33840Sstevel@tonic-gate /*
33850Sstevel@tonic-gate  * hid_polled_input_exit :
33860Sstevel@tonic-gate  *	This is the routine that is called in polled mode  when it is giving up
33870Sstevel@tonic-gate  *	control of the USB keyboard.  This routine, and the lower layer routines
33880Sstevel@tonic-gate  *	that it calls, are responsible for restoring the controller state to the
33890Sstevel@tonic-gate  *	state it was in before polled mode.
33900Sstevel@tonic-gate  */
33910Sstevel@tonic-gate static int
hid_polled_input_exit(hid_polled_handle_t hid_polled_inputp)33920Sstevel@tonic-gate hid_polled_input_exit(hid_polled_handle_t hid_polled_inputp)
33930Sstevel@tonic-gate {
33940Sstevel@tonic-gate 	hid_state_t *hidp = (hid_state_t *)hid_polled_inputp;
33950Sstevel@tonic-gate 
33960Sstevel@tonic-gate 	/*
33970Sstevel@tonic-gate 	 * Call the lower layers to restore any state information.
33980Sstevel@tonic-gate 	 */
33990Sstevel@tonic-gate 	(void) usb_console_input_exit(hidp->hid_polled_console_info);
34000Sstevel@tonic-gate 
34010Sstevel@tonic-gate 	return (0);
34020Sstevel@tonic-gate }
3403