17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5112116d8Sfb  * Common Development and Distribution License (the "License").
6112116d8Sfb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2288447a05SGarrett D'Amore  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * USB audio hid streams module - processes hid data
2988447a05SGarrett D'Amore  * from HID driver and converts to a format that usb_ac
307c478bd9Sstevel@tonic-gate  * understands. The stack looks like this :
31112116d8Sfb  *	hid --> usb_ah --> usb_ac --> audio framework
327c478bd9Sstevel@tonic-gate  * usb_ac just acts as a passthrough layer for the converted data.
337c478bd9Sstevel@tonic-gate  *
3488447a05SGarrett D'Amore  * During open, usb_ah gets the parser handle from hid and gets the
3588447a05SGarrett D'Amore  * hardware information passed as report descriptor. Then it finds out
3688447a05SGarrett D'Amore  * the relevant usages and stores the bitmap and other information in
3788447a05SGarrett D'Amore  * internal data structure. When a button is pressed to. say,
3888447a05SGarrett D'Amore  * increase/decrease the volume, a report is generated and hid sends
3988447a05SGarrett D'Amore  * that data up through the streams. usb_ah, upon getting this
4088447a05SGarrett D'Amore  * information and with the prior knowledge about the bitmap for each
4188447a05SGarrett D'Amore  * button, calculates the value and sends up to usb_ac.  usb_ac in
4288447a05SGarrett D'Amore  * turn sends a command down to speaker to increase the volume of the
4388447a05SGarrett D'Amore  * speaker that is managed by usb_ac.
447c478bd9Sstevel@tonic-gate  */
457c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
467c478bd9Sstevel@tonic-gate #include <sys/usb/clients/hid/hid.h>
477c478bd9Sstevel@tonic-gate #include <sys/usb/clients/hidparser/hidparser.h>
487c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
49d29f5a71Szhigang lu - Sun Microsystems - Beijing China #include <sys/strsun.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include <sys/usb/clients/audio/usb_audio.h>
537c478bd9Sstevel@tonic-gate #include <sys/usb/clients/audio/usb_mixer.h>
547c478bd9Sstevel@tonic-gate #include <sys/usb/clients/audio/usb_ah/usb_ah.h>
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /* debugging information */
574610e4a0Sfrits uint_t			usb_ah_errmask = (uint_t)PRINT_MASK_ALL;
584610e4a0Sfrits uint_t			usb_ah_errlevel = USB_LOG_L4;
594610e4a0Sfrits static usb_log_handle_t	usb_ah_log_handle;
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate /*
627c478bd9Sstevel@tonic-gate  * Internal Function Prototypes
637c478bd9Sstevel@tonic-gate  */
647c478bd9Sstevel@tonic-gate static void	usb_ah_mctl_receive(queue_t *, mblk_t *);
657c478bd9Sstevel@tonic-gate static mblk_t	*usb_ah_cp_mblk(mblk_t *);
667c478bd9Sstevel@tonic-gate static void	usb_ah_timeout(void *);
677c478bd9Sstevel@tonic-gate static void	usb_ah_repeat_send(usb_ah_state_t *, usb_ah_button_descr_t *,
687c478bd9Sstevel@tonic-gate 			struct iocblk, char *, int);
697c478bd9Sstevel@tonic-gate static void	usb_ah_cancel_timeout(usb_ah_state_t *);
707c478bd9Sstevel@tonic-gate static void	usb_ah_check_usage_send_data(usb_ah_state_t *, mblk_t *);
717c478bd9Sstevel@tonic-gate static int	usb_ah_get_cooked_rd(usb_ah_state_t *);
7288447a05SGarrett D'Amore static mblk_t	*usb_ah_mk_mctl(struct iocblk, void *, size_t);
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /* stream qinit functions defined here */
757c478bd9Sstevel@tonic-gate static int	usb_ah_open(queue_t *, dev_t *, int, int, cred_t *);
767c478bd9Sstevel@tonic-gate static int	usb_ah_close(queue_t *, int, cred_t *);
7788447a05SGarrett D'Amore static int	usb_ah_rput(queue_t *, mblk_t *);
78*aef5ddefSToomas Soome static int	usb_ah_wput(queue_t *, mblk_t *);
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate /*
817c478bd9Sstevel@tonic-gate  * Global Variables
827c478bd9Sstevel@tonic-gate  */
837c478bd9Sstevel@tonic-gate int usb_ah_rpt_tick;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate static struct streamtab usb_ah_info;
867c478bd9Sstevel@tonic-gate static struct fmodsw fsw = {
877c478bd9Sstevel@tonic-gate 	"usb_ah",
887c478bd9Sstevel@tonic-gate 	&usb_ah_info,
897c478bd9Sstevel@tonic-gate 	D_NEW | D_MP | D_MTPERMOD
907c478bd9Sstevel@tonic-gate };
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate /*
937c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
947c478bd9Sstevel@tonic-gate  */
957c478bd9Sstevel@tonic-gate extern struct mod_ops mod_strmodops;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate static struct modlstrmod modlstrmod = {
987c478bd9Sstevel@tonic-gate 	&mod_strmodops,
9977e51571Sgongtian zhao - Sun Microsystems - Beijing China 	"USB audio hid streams",
1007c478bd9Sstevel@tonic-gate 	&fsw
1017c478bd9Sstevel@tonic-gate };
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1047c478bd9Sstevel@tonic-gate 	MODREV_1,
1057c478bd9Sstevel@tonic-gate 	(void *)&modlstrmod,
1067c478bd9Sstevel@tonic-gate 	NULL
1077c478bd9Sstevel@tonic-gate };
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /*
1107c478bd9Sstevel@tonic-gate  * Warlock is not aware of the automatic locking mechanisms for
1117c478bd9Sstevel@tonic-gate  * streams modules.
1127c478bd9Sstevel@tonic-gate  * Since warlock is not aware of the streams perimeters, these notes
1137c478bd9Sstevel@tonic-gate  * have been added.
1147c478bd9Sstevel@tonic-gate  */
1157c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk))
1167c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", datab))
1177c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", msgb))
1187c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", queue))
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate /*
1217c478bd9Sstevel@tonic-gate  * Module qinit functions
1227c478bd9Sstevel@tonic-gate  */
1237c478bd9Sstevel@tonic-gate static struct module_info usb_ah_minfo = {
1247c478bd9Sstevel@tonic-gate 	0,		/* module id number */
1257c478bd9Sstevel@tonic-gate 	"usb_ah",	/* module name */
1267c478bd9Sstevel@tonic-gate 	0,		/* min packet size accepted */
1277c478bd9Sstevel@tonic-gate 	INFPSZ,		/* max packet size accepted */
1287c478bd9Sstevel@tonic-gate 	2048,		/* hi-water mark */
1297c478bd9Sstevel@tonic-gate 	128		/* lo-water mark */
1307c478bd9Sstevel@tonic-gate 	};
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate /* read side for key data and ioctl replies */
1337c478bd9Sstevel@tonic-gate static struct qinit usb_ah_rinit = {
13488447a05SGarrett D'Amore 	usb_ah_rput,
13588447a05SGarrett D'Amore 	NULL,		/* service not used */
1367c478bd9Sstevel@tonic-gate 	usb_ah_open,
1377c478bd9Sstevel@tonic-gate 	usb_ah_close,
13888447a05SGarrett D'Amore 	NULL,
1397c478bd9Sstevel@tonic-gate 	&usb_ah_minfo
1407c478bd9Sstevel@tonic-gate 	};
1417c478bd9Sstevel@tonic-gate 
14288447a05SGarrett D'Amore /* write side -- just pass everything down */
1437c478bd9Sstevel@tonic-gate static struct qinit usb_ah_winit = {
144*aef5ddefSToomas Soome 	usb_ah_wput,
14588447a05SGarrett D'Amore 	NULL,
1467c478bd9Sstevel@tonic-gate 	usb_ah_open,
1477c478bd9Sstevel@tonic-gate 	usb_ah_close,
14888447a05SGarrett D'Amore 	NULL,
1497c478bd9Sstevel@tonic-gate 	&usb_ah_minfo
1507c478bd9Sstevel@tonic-gate 	};
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate static struct streamtab usb_ah_info = {
1537c478bd9Sstevel@tonic-gate 	&usb_ah_rinit,
1547c478bd9Sstevel@tonic-gate 	&usb_ah_winit,
1557c478bd9Sstevel@tonic-gate 	NULL,		/* for muxes */
1567c478bd9Sstevel@tonic-gate 	NULL,		/* for muxes */
1577c478bd9Sstevel@tonic-gate };
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate int
_init()1617c478bd9Sstevel@tonic-gate _init()
1627c478bd9Sstevel@tonic-gate {
1637c478bd9Sstevel@tonic-gate 	int rval = mod_install(&modlinkage);
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	if (rval == 0) {
1667c478bd9Sstevel@tonic-gate 		usb_ah_rpt_tick = drv_usectohz(USB_AH_TIMEOUT);
1677c478bd9Sstevel@tonic-gate 		usb_ah_log_handle = usb_alloc_log_hdl(NULL, "usb_ah",
1687c478bd9Sstevel@tonic-gate 		    &usb_ah_errlevel, &usb_ah_errmask, NULL, 0);
1697c478bd9Sstevel@tonic-gate 	}
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	return (rval);
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate int
_fini()1767c478bd9Sstevel@tonic-gate _fini()
1777c478bd9Sstevel@tonic-gate {
1787c478bd9Sstevel@tonic-gate 	int rval = mod_remove(&modlinkage);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	if (rval == 0) {
1817c478bd9Sstevel@tonic-gate 		usb_free_log_hdl(usb_ah_log_handle);
1827c478bd9Sstevel@tonic-gate 	}
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	return (rval);
1857c478bd9Sstevel@tonic-gate }
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)1897c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1907c478bd9Sstevel@tonic-gate {
1917c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1927c478bd9Sstevel@tonic-gate }
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate /*
1967c478bd9Sstevel@tonic-gate  * usb_ah_open :
1977c478bd9Sstevel@tonic-gate  *	Open a usb audio hid device
1987c478bd9Sstevel@tonic-gate  */
1997c478bd9Sstevel@tonic-gate /* ARGSUSED */
2007c478bd9Sstevel@tonic-gate static int
usb_ah_open(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * crp)2017c478bd9Sstevel@tonic-gate usb_ah_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate 	usb_ah_state_t	*usb_ahd;
2047c478bd9Sstevel@tonic-gate 	hidparser_packet_info_t hpack;
2057c478bd9Sstevel@tonic-gate 	struct iocblk	mctlmsg;
2067c478bd9Sstevel@tonic-gate 	mblk_t		*mctl_ptr;
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	if (q->q_ptr) {
209112116d8Sfb 		USB_DPRINTF_L3(PRINT_MASK_OPEN, usb_ah_log_handle,
210112116d8Sfb 		    "usb_ah_open already opened");
2117c478bd9Sstevel@tonic-gate 
212112116d8Sfb 		return (0); /* already opened */
2137c478bd9Sstevel@tonic-gate 	}
2147c478bd9Sstevel@tonic-gate 
21588447a05SGarrett D'Amore 	if (sflag != MODOPEN) {
21688447a05SGarrett D'Amore 		/* Only module open supported */
2177c478bd9Sstevel@tonic-gate 		return (EINVAL);
2187c478bd9Sstevel@tonic-gate 	}
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	usb_ahd = kmem_zalloc(sizeof (usb_ah_state_t), KM_SLEEP);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_OPEN, usb_ah_log_handle,
223112116d8Sfb 	    "usb_ah_state= 0x%p", (void *)usb_ahd);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	mutex_init(&usb_ahd->usb_ah_mutex, NULL, MUTEX_DRIVER, NULL);
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	/*
2287c478bd9Sstevel@tonic-gate 	 * Set up private data.
2297c478bd9Sstevel@tonic-gate 	 */
2307c478bd9Sstevel@tonic-gate 	usb_ahd->usb_ah_readq = q;
2317c478bd9Sstevel@tonic-gate 	usb_ahd->usb_ah_writeq = WR(q);
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	/*
2347c478bd9Sstevel@tonic-gate 	 * Set up queue pointers, so that the "put" procedure will accept
2357c478bd9Sstevel@tonic-gate 	 * the reply to the "ioctl" message we send down.
2367c478bd9Sstevel@tonic-gate 	 */
2377c478bd9Sstevel@tonic-gate 	q->q_ptr = (caddr_t)usb_ahd;
2387c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = (caddr_t)usb_ahd;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	qprocson(q);
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	/* request hid report descriptor from HID */
2437c478bd9Sstevel@tonic-gate 	mctlmsg.ioc_cmd = HID_GET_PARSER_HANDLE;
2447c478bd9Sstevel@tonic-gate 	mctlmsg.ioc_count = 0;
2457c478bd9Sstevel@tonic-gate 	mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
2467c478bd9Sstevel@tonic-gate 	if (mctl_ptr == NULL) {
2477c478bd9Sstevel@tonic-gate 		/* failure to allocate M_CTL message */
2487c478bd9Sstevel@tonic-gate 		qprocsoff(q);
2497c478bd9Sstevel@tonic-gate 		mutex_destroy(&usb_ahd->usb_ah_mutex);
2507c478bd9Sstevel@tonic-gate 		kmem_free(usb_ahd, sizeof (*usb_ahd));
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 		return (ENOMEM);
2537c478bd9Sstevel@tonic-gate 	}
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	putnext(usb_ahd->usb_ah_writeq, mctl_ptr);
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	/*
2587c478bd9Sstevel@tonic-gate 	 * Now that signal has been sent, wait for report descriptor.
2597c478bd9Sstevel@tonic-gate 	 * Cleanup  if user signals in the mean time
2607c478bd9Sstevel@tonic-gate 	 */
2617c478bd9Sstevel@tonic-gate 	usb_ahd->usb_ah_flags |= USB_AH_QWAIT;
2627c478bd9Sstevel@tonic-gate 	while (usb_ahd->usb_ah_flags & USB_AH_QWAIT) {
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 		if (qwait_sig(q) == 0) {
2657c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_flags = 0;
2667c478bd9Sstevel@tonic-gate 			qprocsoff(q);
2677c478bd9Sstevel@tonic-gate 			mutex_destroy(&usb_ahd->usb_ah_mutex);
2687c478bd9Sstevel@tonic-gate 			kmem_free(usb_ahd, sizeof (*usb_ahd));
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 			return (EINTR);
2717c478bd9Sstevel@tonic-gate 		}
2727c478bd9Sstevel@tonic-gate 	}
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	if (usb_ahd->usb_ah_report_descr != NULL) {
2757c478bd9Sstevel@tonic-gate 		hidparser_find_max_packet_size_from_report_descriptor(
276112116d8Sfb 		    usb_ahd->usb_ah_report_descr, &hpack);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 		/* round up to the nearest byte */
2797c478bd9Sstevel@tonic-gate 		usb_ahd->usb_ah_packet_size = (hpack.max_packet_size + 7) / 8;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 		if (hpack.report_id == HID_REPORT_ID_UNDEFINED) {
2827c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_uses_report_ids = 0;
2837c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_report_id = HID_REPORT_ID_UNDEFINED;
2847c478bd9Sstevel@tonic-gate 		} else {
2857c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_uses_report_ids = 1;
2867c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_report_id = hpack.report_id;
2877c478bd9Sstevel@tonic-gate 			/* add more more byte for report id */
2887c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_packet_size++;
2897c478bd9Sstevel@tonic-gate 		}
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 		if (usb_ah_get_cooked_rd(usb_ahd) != USB_SUCCESS) {
2927c478bd9Sstevel@tonic-gate 			qprocsoff(q);
2937c478bd9Sstevel@tonic-gate 			mutex_destroy(&usb_ahd->usb_ah_mutex);
2947c478bd9Sstevel@tonic-gate 			kmem_free(usb_ahd, sizeof (*usb_ahd));
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 			return (EIO);
2977c478bd9Sstevel@tonic-gate 		}
2987c478bd9Sstevel@tonic-gate 	} else {
2997c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_OPEN, usb_ah_log_handle,
3007c478bd9Sstevel@tonic-gate 		    "usb_ah: Invalid Report Descriptor Tree.");
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 		qprocsoff(q);
3037c478bd9Sstevel@tonic-gate 		mutex_destroy(&usb_ahd->usb_ah_mutex);
3047c478bd9Sstevel@tonic-gate 		kmem_free(usb_ahd, sizeof (*usb_ahd));
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 		return (EIO);
3077c478bd9Sstevel@tonic-gate 	}
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	usb_ahd->usb_ah_flags |= USB_AH_OPEN;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	return (0);
3127c478bd9Sstevel@tonic-gate }
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate /*
3167c478bd9Sstevel@tonic-gate  * usb_ah_close :
3177c478bd9Sstevel@tonic-gate  *	Close a audio hid device
3187c478bd9Sstevel@tonic-gate  */
3197c478bd9Sstevel@tonic-gate /* ARGSUSED1 */
3207c478bd9Sstevel@tonic-gate static int
usb_ah_close(queue_t * q,int flag,cred_t * crp)321*aef5ddefSToomas Soome usb_ah_close(queue_t *q, int flag, cred_t *crp)
3227c478bd9Sstevel@tonic-gate {
3237c478bd9Sstevel@tonic-gate 	usb_ah_state_t *usb_ahd = (usb_ah_state_t *)q->q_ptr;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	mutex_enter(&usb_ahd->usb_ah_mutex);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	/*
3287c478bd9Sstevel@tonic-gate 	 * Since we're about to destroy our private data, turn off
3297c478bd9Sstevel@tonic-gate 	 * our open flag first, so we don't accept any more input
3307c478bd9Sstevel@tonic-gate 	 * and try to use that data.
3317c478bd9Sstevel@tonic-gate 	 */
3327c478bd9Sstevel@tonic-gate 	usb_ahd->usb_ah_flags = 0;
3337c478bd9Sstevel@tonic-gate 	usb_ah_cancel_timeout(usb_ahd);
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	flushq(q, FLUSHALL);
3367c478bd9Sstevel@tonic-gate 	flushq(WR(q), FLUSHALL);
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	mutex_exit(&usb_ahd->usb_ah_mutex);
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 	qprocsoff(q);
3417c478bd9Sstevel@tonic-gate 	q->q_ptr = NULL;
3427c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	mutex_destroy(&usb_ahd->usb_ah_mutex);
3457c478bd9Sstevel@tonic-gate 	kmem_free(usb_ahd, sizeof (usb_ah_state_t));
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	return (0);
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
350*aef5ddefSToomas Soome static int
usb_ah_wput(queue_t * q,mblk_t * mp)351*aef5ddefSToomas Soome usb_ah_wput(queue_t *q, mblk_t *mp)
352*aef5ddefSToomas Soome {
353*aef5ddefSToomas Soome 	putnext(q, mp);
354*aef5ddefSToomas Soome 	return (0);
355*aef5ddefSToomas Soome }
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate /*
3587c478bd9Sstevel@tonic-gate  * usb_ah_rput :
3597c478bd9Sstevel@tonic-gate  *	Put procedure for input from driver end of stream (read queue).
3607c478bd9Sstevel@tonic-gate  */
36188447a05SGarrett D'Amore static int
usb_ah_rput(queue_t * q,mblk_t * mp)362*aef5ddefSToomas Soome usb_ah_rput(queue_t *q, mblk_t *mp)
3637c478bd9Sstevel@tonic-gate {
3647c478bd9Sstevel@tonic-gate 	usb_ah_state_t		*usb_ahd;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	usb_ahd = (usb_ah_state_t *)q->q_ptr;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	if (usb_ahd == 0) {
3697c478bd9Sstevel@tonic-gate 		freemsg(mp);	/* nobody's listening */
3707c478bd9Sstevel@tonic-gate 
37188447a05SGarrett D'Amore 		return (0);
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	case M_DATA:
3777c478bd9Sstevel@tonic-gate 		if (!(usb_ahd->usb_ah_flags & USB_AH_OPEN)) {
3787c478bd9Sstevel@tonic-gate 			freemsg(mp);	/* not ready to listen */
3797c478bd9Sstevel@tonic-gate 
380d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		} else if (MBLKL(mp) == usb_ahd->usb_ah_packet_size) {
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 			/*
3837c478bd9Sstevel@tonic-gate 			 * Process this report if the device doesn't have
3847c478bd9Sstevel@tonic-gate 			 * multiple reports, or this is the one we support
3857c478bd9Sstevel@tonic-gate 			 */
3867c478bd9Sstevel@tonic-gate 			if ((usb_ahd->usb_ah_report_id ==
3877c478bd9Sstevel@tonic-gate 			    HID_REPORT_ID_UNDEFINED) ||
3887c478bd9Sstevel@tonic-gate 			    (usb_ahd->usb_ah_report_id == (int)*mp->b_rptr)) {
3897c478bd9Sstevel@tonic-gate 				/* we now have a complete packet */
3907c478bd9Sstevel@tonic-gate 				usb_ah_check_usage_send_data(usb_ahd, mp);
3917c478bd9Sstevel@tonic-gate 			} else {
3927c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(PRINT_MASK_ALL,
3937c478bd9Sstevel@tonic-gate 				    usb_ah_log_handle,
3947c478bd9Sstevel@tonic-gate 				    "usb_ah_rput: skipping report with "
3957c478bd9Sstevel@tonic-gate 				    "id= %d", *mp->b_rptr);
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 				/* skip the reports we don't support */
3987c478bd9Sstevel@tonic-gate 				freemsg(mp);
3997c478bd9Sstevel@tonic-gate 			}
4007c478bd9Sstevel@tonic-gate 		} else {
4017c478bd9Sstevel@tonic-gate 			/* filter out spurious packets */
4027c478bd9Sstevel@tonic-gate 			freemsg(mp);
4037c478bd9Sstevel@tonic-gate 		}
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 		break;
40688447a05SGarrett D'Amore 
4077c478bd9Sstevel@tonic-gate 	case M_CTL:
4087c478bd9Sstevel@tonic-gate 		usb_ah_mctl_receive(q, mp);
40988447a05SGarrett D'Amore 		break;
4107c478bd9Sstevel@tonic-gate 
41188447a05SGarrett D'Amore 	case M_FLUSH:
4127c478bd9Sstevel@tonic-gate 	case M_IOCACK:
4137c478bd9Sstevel@tonic-gate 	case M_IOCNAK:
4147c478bd9Sstevel@tonic-gate 		putnext(q, mp);
41588447a05SGarrett D'Amore 		break;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	default:
4187c478bd9Sstevel@tonic-gate 		putnext(q, mp);
41988447a05SGarrett D'Amore 		break;
4207c478bd9Sstevel@tonic-gate 	}
42188447a05SGarrett D'Amore 
42288447a05SGarrett D'Amore 	return (0);
4237c478bd9Sstevel@tonic-gate }
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate /*
4277c478bd9Sstevel@tonic-gate  * usb_ah_mctl_receive :
4287c478bd9Sstevel@tonic-gate  *	Handle M_CTL messages from hid. If we don't understand
4297c478bd9Sstevel@tonic-gate  *	the command, send it up.
4307c478bd9Sstevel@tonic-gate  */
4317c478bd9Sstevel@tonic-gate static void
usb_ah_mctl_receive(queue_t * q,mblk_t * mp)432*aef5ddefSToomas Soome usb_ah_mctl_receive(queue_t *q, mblk_t *mp)
4337c478bd9Sstevel@tonic-gate {
434*aef5ddefSToomas Soome 	usb_ah_state_t *usb_ahd = (usb_ah_state_t *)q->q_ptr;
435*aef5ddefSToomas Soome 	struct iocblk *iocp;
4367c478bd9Sstevel@tonic-gate 	caddr_t  data;
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
4397c478bd9Sstevel@tonic-gate 	if (mp->b_cont != NULL)
4407c478bd9Sstevel@tonic-gate 		data = (caddr_t)mp->b_cont->b_rptr;
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
4437c478bd9Sstevel@tonic-gate 	case HID_GET_PARSER_HANDLE:
4447c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ALL, usb_ah_log_handle,
4457c478bd9Sstevel@tonic-gate 		    "usb_ah_mctl_receive HID_GET_PARSER_HANDL mctl");
4467c478bd9Sstevel@tonic-gate 		if ((data != NULL) &&
4477c478bd9Sstevel@tonic-gate 		    (iocp->ioc_count == sizeof (hidparser_handle_t)) &&
448d29f5a71Szhigang lu - Sun Microsystems - Beijing China 		    (MBLKL(mp->b_cont) == iocp->ioc_count)) {
4497c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_report_descr =
4507c478bd9Sstevel@tonic-gate 			    *(hidparser_handle_t *)data;
4517c478bd9Sstevel@tonic-gate 		} else {
4527c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_report_descr = NULL;
4537c478bd9Sstevel@tonic-gate 		}
4547c478bd9Sstevel@tonic-gate 		freemsg(mp);
4557c478bd9Sstevel@tonic-gate 		usb_ahd->usb_ah_flags &= ~USB_AH_QWAIT;
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 		break;
4587c478bd9Sstevel@tonic-gate 	case HID_DISCONNECT_EVENT :
4597c478bd9Sstevel@tonic-gate 	case HID_POWER_OFF:
4607c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ALL, usb_ah_log_handle,
4617c478bd9Sstevel@tonic-gate 		    "usb_ah_mctl_receive HID_DISCONNECT_EVENT/HID_POWER_OFF");
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 		/* Cancel any auto repeat keys */
4647c478bd9Sstevel@tonic-gate 		usb_ah_cancel_timeout(usb_ahd);
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 		freemsg(mp);
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 		break;
4697c478bd9Sstevel@tonic-gate 	case HID_CONNECT_EVENT:
4707c478bd9Sstevel@tonic-gate 	case HID_FULL_POWER:
4717c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ALL, usb_ah_log_handle,
4727c478bd9Sstevel@tonic-gate 		    "usb_ah_mctl_receive HID_CONNECT_EVENT/HID_FULL_POWER");
4737c478bd9Sstevel@tonic-gate 		freemsg(mp);
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 		break;
4767c478bd9Sstevel@tonic-gate 	default:
4777c478bd9Sstevel@tonic-gate 		putnext(q, mp);
4787c478bd9Sstevel@tonic-gate 	}
4797c478bd9Sstevel@tonic-gate }
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate /*
4837c478bd9Sstevel@tonic-gate  * usb_ah_repeat_send
4847c478bd9Sstevel@tonic-gate  *	This function sends a M_CTL message to usb_ac repeatedly
4857c478bd9Sstevel@tonic-gate  */
4867c478bd9Sstevel@tonic-gate static void
usb_ah_repeat_send(usb_ah_state_t * usb_ahd,usb_ah_button_descr_t * bd,struct iocblk mctlmsg,char * buf,int len)4877c478bd9Sstevel@tonic-gate usb_ah_repeat_send(usb_ah_state_t *usb_ahd, usb_ah_button_descr_t *bd,
488*aef5ddefSToomas Soome     struct iocblk mctlmsg, char *buf, int len)
4897c478bd9Sstevel@tonic-gate {
4907c478bd9Sstevel@tonic-gate 	mblk_t	*dup_mp;
4917c478bd9Sstevel@tonic-gate 
49288447a05SGarrett D'Amore 	bd->mblk = usb_ah_mk_mctl(mctlmsg, buf, len);
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	if (bd->mblk != NULL) {
4957c478bd9Sstevel@tonic-gate 		dup_mp = usb_ah_cp_mblk(bd->mblk);
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 		if (dup_mp != NULL) {
4987c478bd9Sstevel@tonic-gate 			mutex_exit(&usb_ahd->usb_ah_mutex);
4997c478bd9Sstevel@tonic-gate 			putnext(usb_ahd->usb_ah_readq, dup_mp);
5007c478bd9Sstevel@tonic-gate 			mutex_enter(&usb_ahd->usb_ah_mutex);
5017c478bd9Sstevel@tonic-gate 		}
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 		usb_ahd->usb_ah_cur_bd = bd;
5047c478bd9Sstevel@tonic-gate 		usb_ahd->usb_ah_tid = qtimeout(usb_ahd->usb_ah_readq,
5057c478bd9Sstevel@tonic-gate 		    usb_ah_timeout, bd, usb_ah_rpt_tick);
5067c478bd9Sstevel@tonic-gate 	}
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate /*
5117c478bd9Sstevel@tonic-gate  * usb_ah_timeout:
5127c478bd9Sstevel@tonic-gate  *	Timeout routine to handle autorepeat of buttons
5137c478bd9Sstevel@tonic-gate  */
5147c478bd9Sstevel@tonic-gate static void
usb_ah_timeout(void * addr)5157c478bd9Sstevel@tonic-gate usb_ah_timeout(void *addr)
5167c478bd9Sstevel@tonic-gate {
5177c478bd9Sstevel@tonic-gate 	usb_ah_button_descr_t *bd;
5187c478bd9Sstevel@tonic-gate 	usb_ah_state_t	*usb_ahd;
5197c478bd9Sstevel@tonic-gate 	mblk_t		*dup_mp;
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	bd = (usb_ah_button_descr_t *)addr;
5227c478bd9Sstevel@tonic-gate 	usb_ahd = (usb_ah_state_t *)bd->uahp;
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	mutex_enter(&usb_ahd->usb_ah_mutex);
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 	/*
5277c478bd9Sstevel@tonic-gate 	 * If a release event still hasn't reached, tid will be non-zero
5287c478bd9Sstevel@tonic-gate 	 * Send another press event up
5297c478bd9Sstevel@tonic-gate 	 */
5307c478bd9Sstevel@tonic-gate 	if (usb_ahd->usb_ah_tid) {
5317c478bd9Sstevel@tonic-gate 		dup_mp = usb_ah_cp_mblk(bd->mblk);
5327c478bd9Sstevel@tonic-gate 		if (dup_mp != NULL) {
5337c478bd9Sstevel@tonic-gate 			mutex_exit(&usb_ahd->usb_ah_mutex);
5347c478bd9Sstevel@tonic-gate 			putnext(usb_ahd->usb_ah_readq, dup_mp);
5357c478bd9Sstevel@tonic-gate 			mutex_enter(&usb_ahd->usb_ah_mutex);
5367c478bd9Sstevel@tonic-gate 		}
5377c478bd9Sstevel@tonic-gate 		if (bd->mblk != NULL) {
5387c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_cur_bd = bd;
5397c478bd9Sstevel@tonic-gate 			usb_ahd->usb_ah_tid = qtimeout(usb_ahd->usb_ah_readq,
5407c478bd9Sstevel@tonic-gate 			    usb_ah_timeout, bd, usb_ah_rpt_tick);
5417c478bd9Sstevel@tonic-gate 		}
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 	mutex_exit(&usb_ahd->usb_ah_mutex);
5447c478bd9Sstevel@tonic-gate }
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate /*
5487c478bd9Sstevel@tonic-gate  * usb_ah_cancel_timeout:
5497c478bd9Sstevel@tonic-gate  *	Cancels the timeout for autorepeat sequence
5507c478bd9Sstevel@tonic-gate  */
5517c478bd9Sstevel@tonic-gate static void
usb_ah_cancel_timeout(usb_ah_state_t * usb_ahd)5527c478bd9Sstevel@tonic-gate usb_ah_cancel_timeout(usb_ah_state_t *usb_ahd)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate 	queue_t	*rq = usb_ahd->usb_ah_readq;
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	if (usb_ahd->usb_ah_tid) {
5577c478bd9Sstevel@tonic-gate 		(void) quntimeout(rq, usb_ahd->usb_ah_tid);
5587c478bd9Sstevel@tonic-gate 		usb_ahd->usb_ah_tid = 0;
5597c478bd9Sstevel@tonic-gate 		usb_ahd->usb_ah_cur_bd->pressed = 0;
5607c478bd9Sstevel@tonic-gate 		freemsg(usb_ahd->usb_ah_cur_bd->mblk);
5617c478bd9Sstevel@tonic-gate 		usb_ahd->usb_ah_cur_bd = NULL;
5627c478bd9Sstevel@tonic-gate 	}
5637c478bd9Sstevel@tonic-gate }
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate  * usb_ah_cp_mblk
5687c478bd9Sstevel@tonic-gate  *	Create an identical 2-mblk as the one passed through argument
5697c478bd9Sstevel@tonic-gate  */
5707c478bd9Sstevel@tonic-gate static mblk_t *
usb_ah_cp_mblk(mblk_t * mp)5717c478bd9Sstevel@tonic-gate usb_ah_cp_mblk(mblk_t *mp)
5727c478bd9Sstevel@tonic-gate {
5737c478bd9Sstevel@tonic-gate 	mblk_t *bp1, *bp2;
5747c478bd9Sstevel@tonic-gate 	int len;
575112116d8Sfb 	struct iocblk	*iocp;
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	if ((bp1 = allocb((int)sizeof (struct iocblk), BPRI_HI)) == NULL) {
5787c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
5797c478bd9Sstevel@tonic-gate 		    "usb_ah_cp_mblk: 1st allocb failed");
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 		return (NULL);
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
5857c478bd9Sstevel@tonic-gate 	bcopy(iocp, (struct iocblk *)bp1->b_datap->db_base,
5867c478bd9Sstevel@tonic-gate 	    sizeof (struct iocblk));
5877c478bd9Sstevel@tonic-gate 
5882c8a3792SBinzi Cao - Sun Microsystems - Beijing China 	bp1->b_datap->db_type = M_PROTO;
5897c478bd9Sstevel@tonic-gate 	bp1->b_wptr += sizeof (struct iocblk);
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
592d29f5a71Szhigang lu - Sun Microsystems - Beijing China 	len = MBLKL(mp->b_cont);
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 	if (mp->b_cont->b_datap->db_base) {
5957c478bd9Sstevel@tonic-gate 		if ((bp2 = allocb(len, BPRI_HI)) == NULL) {
5967c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
5977c478bd9Sstevel@tonic-gate 			    "usb_ah_cp_mblk: 2nd allocb failed");
5987c478bd9Sstevel@tonic-gate 			freemsg(bp1);
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 			return (NULL);
6017c478bd9Sstevel@tonic-gate 		}
6027c478bd9Sstevel@tonic-gate 		bp1->b_cont = bp2;
6037c478bd9Sstevel@tonic-gate 		bcopy(mp->b_cont->b_datap->db_base, bp2->b_datap->db_base, len);
6047c478bd9Sstevel@tonic-gate 		bp2->b_wptr += len;
6057c478bd9Sstevel@tonic-gate 	}
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	return (bp1);
6087c478bd9Sstevel@tonic-gate }
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate /*
6127c478bd9Sstevel@tonic-gate  * usb_ah_get_cooked_rd:
6137c478bd9Sstevel@tonic-gate  *	Cook the report descriptor by making hidparser calls and
6147c478bd9Sstevel@tonic-gate  *	put them in a library
6157c478bd9Sstevel@tonic-gate  */
6167c478bd9Sstevel@tonic-gate static int
usb_ah_get_cooked_rd(usb_ah_state_t * usb_ahd)6177c478bd9Sstevel@tonic-gate usb_ah_get_cooked_rd(usb_ah_state_t *usb_ahd)
6187c478bd9Sstevel@tonic-gate {
6197c478bd9Sstevel@tonic-gate 	uint_t		location;
6207c478bd9Sstevel@tonic-gate 	uint_t		offset, i;
6217c478bd9Sstevel@tonic-gate 	usb_ah_button_descr_t	*bd;
6227c478bd9Sstevel@tonic-gate 	hidparser_usage_info_t	*ud;
6237c478bd9Sstevel@tonic-gate 	usb_ah_rpt_t	*rpt;
6247c478bd9Sstevel@tonic-gate 	hidparser_rpt_t	*hid_rpt;
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT]);
6277c478bd9Sstevel@tonic-gate 	hid_rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT].hid_rpt);
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	if (hidparser_get_usage_list_in_order(
6307c478bd9Sstevel@tonic-gate 	    usb_ahd->usb_ah_report_descr,
6317c478bd9Sstevel@tonic-gate 	    usb_ahd->usb_ah_report_id,
6327c478bd9Sstevel@tonic-gate 	    HIDPARSER_ITEM_INPUT,
6337c478bd9Sstevel@tonic-gate 	    hid_rpt) == HIDPARSER_FAILURE) {
6347c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_OPEN,
6357c478bd9Sstevel@tonic-gate 		    usb_ah_log_handle, "getting usage list in order failed");
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_OPEN, usb_ah_log_handle,
6417c478bd9Sstevel@tonic-gate 	    "usb_ah_open:no. of usages=%d", hid_rpt->no_of_usages);
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	location = offset = 0;
6447c478bd9Sstevel@tonic-gate 	for (i = 0; i < hid_rpt->no_of_usages; i++) {
6457c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_OPEN,
6467c478bd9Sstevel@tonic-gate 		    usb_ah_log_handle, "collection=0x%x, usage=0x%x/0x%x",
6477c478bd9Sstevel@tonic-gate 		    hid_rpt->usage_descr[i].collection_usage,
6487c478bd9Sstevel@tonic-gate 		    hid_rpt->usage_descr[i].usage_page,
6497c478bd9Sstevel@tonic-gate 		    hid_rpt->usage_descr[i].usage_id);
6507c478bd9Sstevel@tonic-gate 		ud = &(hid_rpt->usage_descr[i]);
6517c478bd9Sstevel@tonic-gate 		bd = &(rpt->button_descr[i]);
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 		/* Initialize the variables */
6547c478bd9Sstevel@tonic-gate 		hid_rpt->main_item_value = 0;
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 		/* get input items for each usages */
6577c478bd9Sstevel@tonic-gate 		(void) hidparser_get_main_item_data_descr(
6587c478bd9Sstevel@tonic-gate 		    usb_ahd->usb_ah_report_descr,
6597c478bd9Sstevel@tonic-gate 		    usb_ahd->usb_ah_report_id,
6607c478bd9Sstevel@tonic-gate 		    HIDPARSER_ITEM_INPUT,
6617c478bd9Sstevel@tonic-gate 		    hid_rpt->usage_descr[i].usage_page,
6627c478bd9Sstevel@tonic-gate 		    hid_rpt->usage_descr[i].usage_id,
6637c478bd9Sstevel@tonic-gate 		    &hid_rpt->main_item_value);
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate 		bd->location = location;
6667c478bd9Sstevel@tonic-gate 		bd->offset = offset;
6677c478bd9Sstevel@tonic-gate 		bd->no_of_bits = ud->rptsz;
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
6707c478bd9Sstevel@tonic-gate 		    "byte location %d, bit offset %d", bd->location,
6717c478bd9Sstevel@tonic-gate 		    bd->offset);
6727c478bd9Sstevel@tonic-gate 		offset += ud->rptsz;
6737c478bd9Sstevel@tonic-gate 		while (offset >= 8) {
6747c478bd9Sstevel@tonic-gate 			location++;
6757c478bd9Sstevel@tonic-gate 			offset -= 8;
6767c478bd9Sstevel@tonic-gate 		}
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
6817c478bd9Sstevel@tonic-gate }
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate /*
6857c478bd9Sstevel@tonic-gate  * usb_ah_check_usage_send_data:
6867c478bd9Sstevel@tonic-gate  *	Check if a button is pressed, if so, send the appropriate
6877c478bd9Sstevel@tonic-gate  *	message	up
6887c478bd9Sstevel@tonic-gate  */
6897c478bd9Sstevel@tonic-gate static void
usb_ah_check_usage_send_data(usb_ah_state_t * usb_ahd,mblk_t * mp)6907c478bd9Sstevel@tonic-gate usb_ah_check_usage_send_data(usb_ah_state_t *usb_ahd, mblk_t *mp)
6917c478bd9Sstevel@tonic-gate {
6927c478bd9Sstevel@tonic-gate 	int			i, mask;
6937c478bd9Sstevel@tonic-gate 	char			val;
6947c478bd9Sstevel@tonic-gate 	hidparser_rpt_t		*hid_rpt;
6957c478bd9Sstevel@tonic-gate 	usb_ah_button_descr_t	*bd;
6967c478bd9Sstevel@tonic-gate 	usb_ah_rpt_t		*rpt;
6977c478bd9Sstevel@tonic-gate 	uchar_t			*ptr;
6987c478bd9Sstevel@tonic-gate 	struct iocblk		mctlmsg;
6997c478bd9Sstevel@tonic-gate 	mblk_t			*mctl_ptr;
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 	mutex_enter(&usb_ahd->usb_ah_mutex);
7027c478bd9Sstevel@tonic-gate 	rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT]);
7037c478bd9Sstevel@tonic-gate 	hid_rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT].hid_rpt);
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	for (i = 0; i < hid_rpt->no_of_usages; i++) {
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 		bd = &(rpt->button_descr[i]);
7087c478bd9Sstevel@tonic-gate 		bd->uahp = (void *)usb_ahd;
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL,
7117c478bd9Sstevel@tonic-gate 		    usb_ah_log_handle, "usb_ah_check_usage_send_data:"
7127c478bd9Sstevel@tonic-gate 		    "uses_report_id=%d, location=%d, offset=%d, "
7137c478bd9Sstevel@tonic-gate 		    "no_of_bits=%d", usb_ahd->usb_ah_uses_report_ids,
7147c478bd9Sstevel@tonic-gate 		    bd->location, bd->offset, bd->no_of_bits);
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 		ptr = mp->b_rptr + bd->location;
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 		/* XXX workaround */
7197c478bd9Sstevel@tonic-gate 		if (ptr > mp->b_wptr) {
7207c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ALL,
7217c478bd9Sstevel@tonic-gate 			    usb_ah_log_handle, "usb_ah_check_usage_send_data:"
7227c478bd9Sstevel@tonic-gate 			    "bad report: location=%d", bd->location);
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 			continue;
7257c478bd9Sstevel@tonic-gate 		}
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 		ASSERT(ptr <= mp->b_wptr);
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 		mask = ((1 << bd->no_of_bits) - 1);
7307c478bd9Sstevel@tonic-gate 		val = (char)((*ptr >> bd->offset) & mask);
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ALL,
7337c478bd9Sstevel@tonic-gate 		    usb_ah_log_handle, "usb_ah_check_usage_send_data:"
7347c478bd9Sstevel@tonic-gate 		    "usage=0x%x, "
7357c478bd9Sstevel@tonic-gate 		    "mask=0x%x, val=0x%x", hid_rpt->usage_descr[i].usage_id,
7367c478bd9Sstevel@tonic-gate 		    mask, val);
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 		if (hid_rpt->usage_descr[i].collection_usage !=
7397c478bd9Sstevel@tonic-gate 		    HID_CONSUMER_CONTROL) {
7407c478bd9Sstevel@tonic-gate 			/*
7417c478bd9Sstevel@tonic-gate 			 * skip item in unknown collections, for now.
7427c478bd9Sstevel@tonic-gate 			 * this includes the volume and mute controls
7437c478bd9Sstevel@tonic-gate 			 * in the microphone collection on plantronics
7447c478bd9Sstevel@tonic-gate 			 * dsp-300 device with 3.xx firmware.
7457c478bd9Sstevel@tonic-gate 			 */
7467c478bd9Sstevel@tonic-gate 			continue;
7477c478bd9Sstevel@tonic-gate 		}
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 		switch (hid_rpt->usage_descr[i].usage_id) {
7507c478bd9Sstevel@tonic-gate 		case HID_CONSUMER_VOL:	/* LC */
7517c478bd9Sstevel@tonic-gate 			if (val != 0) {
7527c478bd9Sstevel@tonic-gate 				if (hid_rpt->main_item_value &
7537c478bd9Sstevel@tonic-gate 				    HID_MAIN_ITEM_RELATIVE) {
7547c478bd9Sstevel@tonic-gate 					/* Relative volume */
7557c478bd9Sstevel@tonic-gate 					mctlmsg.ioc_cmd = USB_AUDIO_VOL_CHANGE;
7567c478bd9Sstevel@tonic-gate 					mctlmsg.ioc_count = sizeof (uint_t);
75788447a05SGarrett D'Amore 					mctl_ptr = usb_ah_mk_mctl(mctlmsg,
7587c478bd9Sstevel@tonic-gate 					    &val, mctlmsg.ioc_count);
7597c478bd9Sstevel@tonic-gate 					if (mctl_ptr != NULL) {
7607c478bd9Sstevel@tonic-gate 						mutex_exit(&usb_ahd->
761112116d8Sfb 						    usb_ah_mutex);
7627c478bd9Sstevel@tonic-gate 						putnext(usb_ahd->usb_ah_readq,
7637c478bd9Sstevel@tonic-gate 						    mctl_ptr);
7647c478bd9Sstevel@tonic-gate 						mutex_enter(&usb_ahd->
765112116d8Sfb 						    usb_ah_mutex);
7667c478bd9Sstevel@tonic-gate 					}
7677c478bd9Sstevel@tonic-gate 				} else {
7687c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L2(PRINT_MASK_ALL,
7697c478bd9Sstevel@tonic-gate 					    usb_ah_log_handle, "usb_ah_rput:"
7707c478bd9Sstevel@tonic-gate 					    "Absolute volume change "
7717c478bd9Sstevel@tonic-gate 					    "not supported");
7727c478bd9Sstevel@tonic-gate 				}
7737c478bd9Sstevel@tonic-gate 			}
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 			break;
7767c478bd9Sstevel@tonic-gate 		case HID_CONSUMER_VOL_DECR: /* RTC */
7777c478bd9Sstevel@tonic-gate 			if (val != 0) {
7787c478bd9Sstevel@tonic-gate 				val = -val;
7797c478bd9Sstevel@tonic-gate 			}
7807c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
7817c478bd9Sstevel@tonic-gate 		case HID_CONSUMER_VOL_INCR:  /* RTC */
7827c478bd9Sstevel@tonic-gate 			if (val != 0) {
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 				/*
7857c478bd9Sstevel@tonic-gate 				 * If another autorepeating button has been
7867c478bd9Sstevel@tonic-gate 				 * pressed, cancel that one first
7877c478bd9Sstevel@tonic-gate 				 */
7887c478bd9Sstevel@tonic-gate 				usb_ah_cancel_timeout(usb_ahd);
7897c478bd9Sstevel@tonic-gate 				mctlmsg.ioc_cmd = USB_AUDIO_VOL_CHANGE;
7907c478bd9Sstevel@tonic-gate 				mctlmsg.ioc_count = sizeof (uint_t);
7917c478bd9Sstevel@tonic-gate 				bd->pressed = 1;
7927c478bd9Sstevel@tonic-gate 				usb_ah_repeat_send(usb_ahd, bd,
7937c478bd9Sstevel@tonic-gate 				    mctlmsg, (char *)&val, mctlmsg.ioc_count);
7947c478bd9Sstevel@tonic-gate 			} else {
7957c478bd9Sstevel@tonic-gate 				/* Do not steal other's release event */
7967c478bd9Sstevel@tonic-gate 				if (bd->pressed) {
7977c478bd9Sstevel@tonic-gate 					usb_ah_cancel_timeout(usb_ahd);
7987c478bd9Sstevel@tonic-gate 				}
7997c478bd9Sstevel@tonic-gate 			}
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 			break;
8027c478bd9Sstevel@tonic-gate 		case HID_CONSUMER_MUTE:	/* OOC */
8037c478bd9Sstevel@tonic-gate 			if (val) {
8047c478bd9Sstevel@tonic-gate 				mctlmsg.ioc_cmd = USB_AUDIO_MUTE;
8057c478bd9Sstevel@tonic-gate 				mctlmsg.ioc_count = sizeof (uint_t);
80688447a05SGarrett D'Amore 				mctl_ptr = usb_ah_mk_mctl(mctlmsg,
8077c478bd9Sstevel@tonic-gate 				    &val, mctlmsg.ioc_count);
8087c478bd9Sstevel@tonic-gate 				if (mctl_ptr != NULL) {
8097c478bd9Sstevel@tonic-gate 					mutex_exit(&usb_ahd->usb_ah_mutex);
8107c478bd9Sstevel@tonic-gate 					putnext(usb_ahd->usb_ah_readq,
8117c478bd9Sstevel@tonic-gate 					    mctl_ptr);
8127c478bd9Sstevel@tonic-gate 					mutex_enter(&usb_ahd->usb_ah_mutex);
8137c478bd9Sstevel@tonic-gate 				}
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 			}
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 			break;
8187c478bd9Sstevel@tonic-gate 		case HID_CONSUMER_BASS:
8197c478bd9Sstevel@tonic-gate 		case HID_CONSUMER_TREBLE:
8207c478bd9Sstevel@tonic-gate 		default:
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 			break;
8237c478bd9Sstevel@tonic-gate 		}
8247c478bd9Sstevel@tonic-gate 	}
8257c478bd9Sstevel@tonic-gate 	mutex_exit(&usb_ahd->usb_ah_mutex);
8267c478bd9Sstevel@tonic-gate 	freemsg(mp);
8277c478bd9Sstevel@tonic-gate }
82888447a05SGarrett D'Amore 
82988447a05SGarrett D'Amore 
83088447a05SGarrett D'Amore /*
83188447a05SGarrett D'Amore  * since usb_ac now uses LDI to access HID streams, we must change the msg
83288447a05SGarrett D'Amore  * type from M_CTL to M_PROTO since the streamhead will not pass M_CTLs up
83388447a05SGarrett D'Amore  */
83488447a05SGarrett D'Amore static mblk_t *
usb_ah_mk_mctl(struct iocblk mctlmsg,void * buf,size_t len)83588447a05SGarrett D'Amore usb_ah_mk_mctl(struct iocblk mctlmsg, void *buf, size_t len)
83688447a05SGarrett D'Amore {
83788447a05SGarrett D'Amore 	mblk_t *mp;
83888447a05SGarrett D'Amore 
83988447a05SGarrett D'Amore 	mp = usba_mk_mctl(mctlmsg, buf, len);
84088447a05SGarrett D'Amore 	if (mp == NULL)
84188447a05SGarrett D'Amore 		return (NULL);
84288447a05SGarrett D'Amore 
84388447a05SGarrett D'Amore 	mp->b_datap->db_type = M_PROTO;
84488447a05SGarrett D'Amore 	return (mp);
84588447a05SGarrett D'Amore }
846