usb_ah.c revision 7492:2387323b838f
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27/*
28 * USB audio hid streams module - processes hid data
29 * from HID driver and converts to a format that SADA
30 * understands. The stack looks like this :
31 *	hid --> usb_ah --> usb_ac --> audio framework
32 * usb_ac just acts as a passthrough layer for the converted data.
33 *
34 * During open, usb_ah gets the parser handle from hid and gets
35 * the hardware information passed as report descriptor. Then
36 * it finds out the relevant usages and stores the bitmap and other
37 * information in internal data structure. When a button is pressed
38 * to. say, increase/decrease the volume, a report is generated and
39 * hid sends that data up through the streams. usb_ah, upon getting
40 * this information and with the prior knowledge about the bitmap
41 * for each button, calculates the value and sends up to SADA
42 * through usb_ac. SADA in turn sends a command down to speaker to
43 * increase the volume of the speaker that is managed by usb_ac.
44 */
45#include <sys/usb/usba.h>
46#include <sys/usb/clients/hid/hid.h>
47#include <sys/usb/clients/hidparser/hidparser.h>
48#include <sys/stropts.h>
49#include <sys/strsun.h>
50
51#include <sys/audio.h>
52#include <sys/audiovar.h>
53#include <sys/audio/audio_support.h>
54#include <sys/audio/audio_src.h>
55#include <sys/mixer.h>
56#include <sys/audio/audio_mixer.h>
57#include <sys/audio/am_src1.h>
58
59#include <sys/usb/clients/audio/usb_audio.h>
60#include <sys/usb/clients/audio/usb_mixer.h>
61#include <sys/usb/clients/audio/usb_ah/usb_ah.h>
62
63/* debugging information */
64uint_t			usb_ah_errmask = (uint_t)PRINT_MASK_ALL;
65uint_t			usb_ah_errlevel = USB_LOG_L4;
66static usb_log_handle_t	usb_ah_log_handle;
67
68/*
69 * Internal Function Prototypes
70 */
71static void	usb_ah_mctl_receive(queue_t *, mblk_t *);
72static mblk_t	*usb_ah_cp_mblk(mblk_t *);
73static void	usb_ah_timeout(void *);
74static void	usb_ah_repeat_send(usb_ah_state_t *, usb_ah_button_descr_t *,
75			struct iocblk, char *, int);
76static void	usb_ah_cancel_timeout(usb_ah_state_t *);
77static void	usb_ah_check_usage_send_data(usb_ah_state_t *, mblk_t *);
78static int	usb_ah_get_cooked_rd(usb_ah_state_t *);
79
80/* stream qinit functions defined here */
81static int	usb_ah_open(queue_t *, dev_t *, int, int, cred_t *);
82static int	usb_ah_close(queue_t *, int, cred_t *);
83static void	usb_ah_wput(queue_t *, mblk_t *);
84static void	usb_ah_rput(queue_t *, mblk_t *);
85
86/*
87 * Global Variables
88 */
89int usb_ah_rpt_tick;
90
91static struct streamtab usb_ah_info;
92static struct fmodsw fsw = {
93	"usb_ah",
94	&usb_ah_info,
95	D_NEW | D_MP | D_MTPERMOD
96};
97
98/*
99 * Module linkage information for the kernel.
100 */
101extern struct mod_ops mod_strmodops;
102
103static struct modlstrmod modlstrmod = {
104	&mod_strmodops,
105	"USB audio hid streams",
106	&fsw
107};
108
109static struct modlinkage modlinkage = {
110	MODREV_1,
111	(void *)&modlstrmod,
112	NULL
113};
114
115/*
116 * Warlock is not aware of the automatic locking mechanisms for
117 * streams modules.
118 * Since warlock is not aware of the streams perimeters, these notes
119 * have been added.
120 */
121_NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk))
122_NOTE(SCHEME_PROTECTS_DATA("unique per call", datab))
123_NOTE(SCHEME_PROTECTS_DATA("unique per call", msgb))
124_NOTE(SCHEME_PROTECTS_DATA("unique per call", queue))
125
126/*
127 * Module qinit functions
128 */
129static struct module_info usb_ah_minfo = {
130	0,		/* module id number */
131	"usb_ah",	/* module name */
132	0,		/* min packet size accepted */
133	INFPSZ,		/* max packet size accepted */
134	2048,		/* hi-water mark */
135	128		/* lo-water mark */
136	};
137
138/* read side for key data and ioctl replies */
139static struct qinit usb_ah_rinit = {
140	(int (*)())usb_ah_rput,
141	(int (*)())NULL,		/* service not used */
142	usb_ah_open,
143	usb_ah_close,
144	(int (*)())NULL,
145	&usb_ah_minfo
146	};
147
148/* write side for ioctls */
149static struct qinit usb_ah_winit = {
150	(int (*)())usb_ah_wput,
151	(int (*)())NULL,
152	usb_ah_open,
153	usb_ah_close,
154	(int (*)())NULL,
155	&usb_ah_minfo
156	};
157
158static struct streamtab usb_ah_info = {
159	&usb_ah_rinit,
160	&usb_ah_winit,
161	NULL,		/* for muxes */
162	NULL,		/* for muxes */
163};
164
165
166int
167_init()
168{
169	int rval = mod_install(&modlinkage);
170
171	if (rval == 0) {
172		usb_ah_rpt_tick = drv_usectohz(USB_AH_TIMEOUT);
173		usb_ah_log_handle = usb_alloc_log_hdl(NULL, "usb_ah",
174		    &usb_ah_errlevel, &usb_ah_errmask, NULL, 0);
175	}
176
177	return (rval);
178}
179
180
181int
182_fini()
183{
184	int rval = mod_remove(&modlinkage);
185
186	if (rval == 0) {
187		usb_free_log_hdl(usb_ah_log_handle);
188	}
189
190	return (rval);
191}
192
193
194int
195_info(struct modinfo *modinfop)
196{
197	return (mod_info(&modlinkage, modinfop));
198}
199
200
201/*
202 * usb_ah_open :
203 *	Open a usb audio hid device
204 */
205/* ARGSUSED */
206static int
207usb_ah_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
208{
209	usb_ah_state_t	*usb_ahd;
210	hidparser_packet_info_t hpack;
211	struct iocblk	mctlmsg;
212	mblk_t		*mctl_ptr;
213
214	if (q->q_ptr) {
215		USB_DPRINTF_L3(PRINT_MASK_OPEN, usb_ah_log_handle,
216		    "usb_ah_open already opened");
217
218		return (0); /* already opened */
219	}
220
221	switch (sflag) {
222	case MODOPEN:
223
224		break;
225	case CLONEOPEN:
226		USB_DPRINTF_L3(PRINT_MASK_OPEN, usb_ah_log_handle,
227		    "usb_ah_open: Clone open not supported");
228
229		/* FALLTHRU */
230	default:
231
232		return (EINVAL);
233	}
234
235	usb_ahd = kmem_zalloc(sizeof (usb_ah_state_t), KM_SLEEP);
236
237	USB_DPRINTF_L3(PRINT_MASK_OPEN, usb_ah_log_handle,
238	    "usb_ah_state= 0x%p", (void *)usb_ahd);
239
240	mutex_init(&usb_ahd->usb_ah_mutex, NULL, MUTEX_DRIVER, NULL);
241
242	/*
243	 * Set up private data.
244	 */
245	usb_ahd->usb_ah_readq = q;
246	usb_ahd->usb_ah_writeq = WR(q);
247
248	/*
249	 * Set up queue pointers, so that the "put" procedure will accept
250	 * the reply to the "ioctl" message we send down.
251	 */
252	q->q_ptr = (caddr_t)usb_ahd;
253	WR(q)->q_ptr = (caddr_t)usb_ahd;
254
255	qprocson(q);
256
257	/* request hid report descriptor from HID */
258	mctlmsg.ioc_cmd = HID_GET_PARSER_HANDLE;
259	mctlmsg.ioc_count = 0;
260	mctl_ptr = usba_mk_mctl(mctlmsg, NULL, 0);
261	if (mctl_ptr == NULL) {
262		/* failure to allocate M_CTL message */
263		qprocsoff(q);
264		mutex_destroy(&usb_ahd->usb_ah_mutex);
265		kmem_free(usb_ahd, sizeof (*usb_ahd));
266
267		return (ENOMEM);
268	}
269
270	putnext(usb_ahd->usb_ah_writeq, mctl_ptr);
271
272	/*
273	 * Now that signal has been sent, wait for report descriptor.
274	 * Cleanup  if user signals in the mean time
275	 */
276	usb_ahd->usb_ah_flags |= USB_AH_QWAIT;
277	while (usb_ahd->usb_ah_flags & USB_AH_QWAIT) {
278
279		if (qwait_sig(q) == 0) {
280			usb_ahd->usb_ah_flags = 0;
281			qprocsoff(q);
282			mutex_destroy(&usb_ahd->usb_ah_mutex);
283			kmem_free(usb_ahd, sizeof (*usb_ahd));
284
285			return (EINTR);
286		}
287	}
288
289	if (usb_ahd->usb_ah_report_descr != NULL) {
290		hidparser_find_max_packet_size_from_report_descriptor(
291		    usb_ahd->usb_ah_report_descr, &hpack);
292
293		/* round up to the nearest byte */
294		usb_ahd->usb_ah_packet_size = (hpack.max_packet_size + 7) / 8;
295
296		if (hpack.report_id == HID_REPORT_ID_UNDEFINED) {
297			usb_ahd->usb_ah_uses_report_ids = 0;
298			usb_ahd->usb_ah_report_id = HID_REPORT_ID_UNDEFINED;
299		} else {
300			usb_ahd->usb_ah_uses_report_ids = 1;
301			usb_ahd->usb_ah_report_id = hpack.report_id;
302			/* add more more byte for report id */
303			usb_ahd->usb_ah_packet_size++;
304		}
305
306		if (usb_ah_get_cooked_rd(usb_ahd) != USB_SUCCESS) {
307			qprocsoff(q);
308			mutex_destroy(&usb_ahd->usb_ah_mutex);
309			kmem_free(usb_ahd, sizeof (*usb_ahd));
310
311			return (EIO);
312		}
313	} else {
314		USB_DPRINTF_L2(PRINT_MASK_OPEN, usb_ah_log_handle,
315		    "usb_ah: Invalid Report Descriptor Tree.");
316
317		qprocsoff(q);
318		mutex_destroy(&usb_ahd->usb_ah_mutex);
319		kmem_free(usb_ahd, sizeof (*usb_ahd));
320
321		return (EIO);
322	}
323
324	usb_ahd->usb_ah_flags |= USB_AH_OPEN;
325
326	USB_DPRINTF_L3(PRINT_MASK_OPEN, usb_ah_log_handle,
327	    "usb_ah_open exiting");
328
329	return (0);
330}
331
332
333/*
334 * usb_ah_close :
335 *	Close a audio hid device
336 */
337/* ARGSUSED1 */
338static int
339usb_ah_close(register queue_t *q, int flag, cred_t *crp)
340{
341	usb_ah_state_t *usb_ahd = (usb_ah_state_t *)q->q_ptr;
342
343	mutex_enter(&usb_ahd->usb_ah_mutex);
344
345	/*
346	 * Since we're about to destroy our private data, turn off
347	 * our open flag first, so we don't accept any more input
348	 * and try to use that data.
349	 */
350	usb_ahd->usb_ah_flags = 0;
351	usb_ah_cancel_timeout(usb_ahd);
352
353	flushq(q, FLUSHALL);
354	flushq(WR(q), FLUSHALL);
355
356	mutex_exit(&usb_ahd->usb_ah_mutex);
357
358	qprocsoff(q);
359	q->q_ptr = NULL;
360	WR(q)->q_ptr = NULL;
361
362	mutex_destroy(&usb_ahd->usb_ah_mutex);
363	kmem_free(usb_ahd, sizeof (usb_ah_state_t));
364
365	USB_DPRINTF_L4(PRINT_MASK_CLOSE, usb_ah_log_handle,
366	    "usb_ah_close exiting");
367
368	return (0);
369}
370
371
372/*
373 * usb_ah_wput :
374 *	usb_ah	module output queue put procedure: handles M_IOCTL
375 *	messages.
376 */
377static void
378usb_ah_wput(register queue_t *q, register mblk_t *mp)
379{
380
381	USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
382	    "usb_ah_wput entering");
383
384	switch (mp->b_datap->db_type) {
385	case M_FLUSH:
386		if (*mp->b_rptr & FLUSHW) {
387			flushq(q, FLUSHDATA);
388		}
389		if (*mp->b_rptr & FLUSHR) {
390			flushq(RD(q), FLUSHDATA);
391		}
392
393		break;
394	default:
395		break;
396	}
397
398	putnext(q, mp);
399
400	USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
401	    "usb_ah_wput exiting:3");
402}
403
404
405/*
406 * usb_ah_rput :
407 *	Put procedure for input from driver end of stream (read queue).
408 */
409static void
410usb_ah_rput(register queue_t *q, register mblk_t *mp)
411{
412	usb_ah_state_t		*usb_ahd;
413
414	usb_ahd = (usb_ah_state_t *)q->q_ptr;
415
416	USB_DPRINTF_L3(PRINT_MASK_ALL, usb_ah_log_handle,
417	    "usb_ah_rput: Begin, usb_ah state=0x%p, q=0x%p, mp=0x%p",
418	    (void *)usb_ahd, (void *)q, (void *)mp);
419
420	if (usb_ahd == 0) {
421		freemsg(mp);	/* nobody's listening */
422
423		return;
424	}
425
426	switch (mp->b_datap->db_type) {
427	case M_FLUSH:
428		if (*mp->b_rptr & FLUSHW)
429			flushq(WR(q), FLUSHDATA);
430		if (*mp->b_rptr & FLUSHR)
431			flushq(q, FLUSHDATA);
432		freemsg(mp);
433
434		return;
435	case M_DATA:
436		if (!(usb_ahd->usb_ah_flags & USB_AH_OPEN)) {
437			freemsg(mp);	/* not ready to listen */
438
439			return;
440		} else if (MBLKL(mp) == usb_ahd->usb_ah_packet_size) {
441
442			/*
443			 * Process this report if the device doesn't have
444			 * multiple reports, or this is the one we support
445			 */
446			if ((usb_ahd->usb_ah_report_id ==
447			    HID_REPORT_ID_UNDEFINED) ||
448			    (usb_ahd->usb_ah_report_id == (int)*mp->b_rptr)) {
449				/* we now have a complete packet */
450				usb_ah_check_usage_send_data(usb_ahd, mp);
451			} else {
452				USB_DPRINTF_L2(PRINT_MASK_ALL,
453				    usb_ah_log_handle,
454				    "usb_ah_rput: skipping report with "
455				    "id= %d", *mp->b_rptr);
456
457				/* skip the reports we don't support */
458				freemsg(mp);
459			}
460		} else {
461			/* filter out spurious packets */
462			freemsg(mp);
463		}
464
465		break;
466	case M_CTL:
467		usb_ah_mctl_receive(q, mp);
468
469		return;
470	case M_IOCACK:
471	case M_IOCNAK:
472		putnext(q, mp);
473
474		return;
475	default:
476		putnext(q, mp);
477
478		return;
479	}
480}
481
482
483/*
484 * usb_ah_mctl_receive :
485 *	Handle M_CTL messages from hid. If we don't understand
486 *	the command, send it up.
487 */
488static void
489usb_ah_mctl_receive(register queue_t *q, register mblk_t *mp)
490{
491	register usb_ah_state_t *usb_ahd = (usb_ah_state_t *)q->q_ptr;
492	register struct iocblk *iocp;
493	caddr_t  data;
494
495	iocp = (struct iocblk *)mp->b_rptr;
496	if (mp->b_cont != NULL)
497		data = (caddr_t)mp->b_cont->b_rptr;
498
499	switch (iocp->ioc_cmd) {
500	case HID_GET_PARSER_HANDLE:
501		USB_DPRINTF_L3(PRINT_MASK_ALL, usb_ah_log_handle,
502		    "usb_ah_mctl_receive HID_GET_PARSER_HANDL mctl");
503		if ((data != NULL) &&
504		    (iocp->ioc_count == sizeof (hidparser_handle_t)) &&
505		    (MBLKL(mp->b_cont) == iocp->ioc_count)) {
506			usb_ahd->usb_ah_report_descr =
507			    *(hidparser_handle_t *)data;
508		} else {
509			usb_ahd->usb_ah_report_descr = NULL;
510		}
511		freemsg(mp);
512		usb_ahd->usb_ah_flags &= ~USB_AH_QWAIT;
513
514		break;
515	case HID_DISCONNECT_EVENT :
516	case HID_POWER_OFF:
517		USB_DPRINTF_L3(PRINT_MASK_ALL, usb_ah_log_handle,
518		    "usb_ah_mctl_receive HID_DISCONNECT_EVENT/HID_POWER_OFF");
519
520		/* Cancel any auto repeat keys */
521		usb_ah_cancel_timeout(usb_ahd);
522
523		freemsg(mp);
524
525		break;
526	case HID_CONNECT_EVENT:
527	case HID_FULL_POWER:
528		USB_DPRINTF_L3(PRINT_MASK_ALL, usb_ah_log_handle,
529		    "usb_ah_mctl_receive HID_CONNECT_EVENT/HID_FULL_POWER");
530		freemsg(mp);
531
532		break;
533	default:
534		putnext(q, mp);
535	}
536}
537
538
539/*
540 * usb_ah_repeat_send
541 *	This function sends a M_CTL message to usb_ac repeatedly
542 */
543static void
544usb_ah_repeat_send(usb_ah_state_t *usb_ahd, usb_ah_button_descr_t *bd,
545		struct iocblk mctlmsg, char *buf, int len)
546{
547	mblk_t	*dup_mp;
548
549	bd->mblk = usba_mk_mctl(mctlmsg, buf, len);
550
551	if (bd->mblk != NULL) {
552		dup_mp = usb_ah_cp_mblk(bd->mblk);
553
554		if (dup_mp != NULL) {
555			mutex_exit(&usb_ahd->usb_ah_mutex);
556			putnext(usb_ahd->usb_ah_readq, dup_mp);
557			mutex_enter(&usb_ahd->usb_ah_mutex);
558		}
559
560		usb_ahd->usb_ah_cur_bd = bd;
561		usb_ahd->usb_ah_tid = qtimeout(usb_ahd->usb_ah_readq,
562		    usb_ah_timeout, bd, usb_ah_rpt_tick);
563	}
564}
565
566
567/*
568 * usb_ah_timeout:
569 *	Timeout routine to handle autorepeat of buttons
570 */
571static void
572usb_ah_timeout(void *addr)
573{
574	usb_ah_button_descr_t *bd;
575	usb_ah_state_t	*usb_ahd;
576	mblk_t		*dup_mp;
577
578	bd = (usb_ah_button_descr_t *)addr;
579	usb_ahd = (usb_ah_state_t *)bd->uahp;
580
581	mutex_enter(&usb_ahd->usb_ah_mutex);
582	USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
583	    "usb_ah_timeout: tid=0x%p", usb_ahd->usb_ah_tid);
584
585	/*
586	 * If a release event still hasn't reached, tid will be non-zero
587	 * Send another press event up
588	 */
589	if (usb_ahd->usb_ah_tid) {
590		dup_mp = usb_ah_cp_mblk(bd->mblk);
591		if (dup_mp != NULL) {
592			mutex_exit(&usb_ahd->usb_ah_mutex);
593			putnext(usb_ahd->usb_ah_readq, dup_mp);
594			mutex_enter(&usb_ahd->usb_ah_mutex);
595		}
596		if (bd->mblk != NULL) {
597			usb_ahd->usb_ah_cur_bd = bd;
598			usb_ahd->usb_ah_tid = qtimeout(usb_ahd->usb_ah_readq,
599			    usb_ah_timeout, bd, usb_ah_rpt_tick);
600		}
601	}
602	mutex_exit(&usb_ahd->usb_ah_mutex);
603}
604
605
606/*
607 * usb_ah_cancel_timeout:
608 *	Cancels the timeout for autorepeat sequence
609 */
610static void
611usb_ah_cancel_timeout(usb_ah_state_t *usb_ahd)
612{
613	queue_t	*rq = usb_ahd->usb_ah_readq;
614
615	USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
616	    "usb_ah_cancel_timeout: tid=0x%p", usb_ahd->usb_ah_tid);
617
618	if (usb_ahd->usb_ah_tid) {
619		(void) quntimeout(rq, usb_ahd->usb_ah_tid);
620		usb_ahd->usb_ah_tid = 0;
621		usb_ahd->usb_ah_cur_bd->pressed = 0;
622		freemsg(usb_ahd->usb_ah_cur_bd->mblk);
623		usb_ahd->usb_ah_cur_bd = NULL;
624	}
625}
626
627
628/*
629 * usb_ah_cp_mblk
630 *	Create an identical 2-mblk as the one passed through argument
631 */
632static mblk_t *
633usb_ah_cp_mblk(mblk_t *mp)
634{
635	mblk_t *bp1, *bp2;
636	int len;
637	struct iocblk	*iocp;
638
639	if ((bp1 = allocb((int)sizeof (struct iocblk), BPRI_HI)) == NULL) {
640		USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
641		    "usb_ah_cp_mblk: 1st allocb failed");
642
643		return (NULL);
644	}
645
646	iocp = (struct iocblk *)mp->b_rptr;
647	bcopy(iocp, (struct iocblk *)bp1->b_datap->db_base,
648	    sizeof (struct iocblk));
649
650	bp1->b_datap->db_type = M_CTL;
651	bp1->b_wptr += sizeof (struct iocblk);
652
653	ASSERT(mp->b_cont != NULL);
654	len = MBLKL(mp->b_cont);
655
656	if (mp->b_cont->b_datap->db_base) {
657		if ((bp2 = allocb(len, BPRI_HI)) == NULL) {
658			USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
659			    "usb_ah_cp_mblk: 2nd allocb failed");
660			freemsg(bp1);
661
662			return (NULL);
663		}
664		bp1->b_cont = bp2;
665		bcopy(mp->b_cont->b_datap->db_base, bp2->b_datap->db_base, len);
666		bp2->b_wptr += len;
667	}
668
669	return (bp1);
670}
671
672
673/*
674 * usb_ah_get_cooked_rd:
675 *	Cook the report descriptor by making hidparser calls and
676 *	put them in a library
677 */
678static int
679usb_ah_get_cooked_rd(usb_ah_state_t *usb_ahd)
680{
681	uint_t		location;
682	uint_t		offset, i;
683	usb_ah_button_descr_t	*bd;
684	hidparser_usage_info_t	*ud;
685	usb_ah_rpt_t	*rpt;
686	hidparser_rpt_t	*hid_rpt;
687
688	rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT]);
689	hid_rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT].hid_rpt);
690
691	if (hidparser_get_usage_list_in_order(
692	    usb_ahd->usb_ah_report_descr,
693	    usb_ahd->usb_ah_report_id,
694	    HIDPARSER_ITEM_INPUT,
695	    hid_rpt) == HIDPARSER_FAILURE) {
696		USB_DPRINTF_L3(PRINT_MASK_OPEN,
697		    usb_ah_log_handle, "getting usage list in order failed");
698
699		return (USB_FAILURE);
700	}
701
702	USB_DPRINTF_L4(PRINT_MASK_OPEN, usb_ah_log_handle,
703	    "usb_ah_open:no. of usages=%d", hid_rpt->no_of_usages);
704
705	location = offset = 0;
706	for (i = 0; i < hid_rpt->no_of_usages; i++) {
707		USB_DPRINTF_L4(PRINT_MASK_OPEN,
708		    usb_ah_log_handle, "collection=0x%x, usage=0x%x/0x%x",
709		    hid_rpt->usage_descr[i].collection_usage,
710		    hid_rpt->usage_descr[i].usage_page,
711		    hid_rpt->usage_descr[i].usage_id);
712		ud = &(hid_rpt->usage_descr[i]);
713		bd = &(rpt->button_descr[i]);
714
715		/* Initialize the variables */
716		hid_rpt->main_item_value = 0;
717
718		/* get input items for each usages */
719		(void) hidparser_get_main_item_data_descr(
720		    usb_ahd->usb_ah_report_descr,
721		    usb_ahd->usb_ah_report_id,
722		    HIDPARSER_ITEM_INPUT,
723		    hid_rpt->usage_descr[i].usage_page,
724		    hid_rpt->usage_descr[i].usage_id,
725		    &hid_rpt->main_item_value);
726
727		bd->location = location;
728		bd->offset = offset;
729		bd->no_of_bits = ud->rptsz;
730
731		USB_DPRINTF_L4(PRINT_MASK_ALL, usb_ah_log_handle,
732		    "byte location %d, bit offset %d", bd->location,
733		    bd->offset);
734		offset += ud->rptsz;
735		while (offset >= 8) {
736			location++;
737			offset -= 8;
738		}
739
740	}
741
742	return (USB_SUCCESS);
743}
744
745
746/*
747 * usb_ah_check_usage_send_data:
748 *	Check if a button is pressed, if so, send the appropriate
749 *	message	up
750 */
751static void
752usb_ah_check_usage_send_data(usb_ah_state_t *usb_ahd, mblk_t *mp)
753{
754	int			i, mask;
755	char			val;
756	hidparser_rpt_t		*hid_rpt;
757	usb_ah_button_descr_t	*bd;
758	usb_ah_rpt_t		*rpt;
759	uchar_t			*ptr;
760	struct iocblk		mctlmsg;
761	mblk_t			*mctl_ptr;
762
763	mutex_enter(&usb_ahd->usb_ah_mutex);
764	rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT]);
765	hid_rpt = &(usb_ahd->usb_ah_report[USB_AH_INPUT_RPT].hid_rpt);
766
767	for (i = 0; i < hid_rpt->no_of_usages; i++) {
768
769		bd = &(rpt->button_descr[i]);
770		bd->uahp = (void *)usb_ahd;
771
772		USB_DPRINTF_L4(PRINT_MASK_ALL,
773		    usb_ah_log_handle, "usb_ah_check_usage_send_data:"
774		    "uses_report_id=%d, location=%d, offset=%d, "
775		    "no_of_bits=%d", usb_ahd->usb_ah_uses_report_ids,
776		    bd->location, bd->offset, bd->no_of_bits);
777
778		ptr = mp->b_rptr + bd->location;
779
780		/* XXX workaround */
781		if (ptr > mp->b_wptr) {
782			USB_DPRINTF_L2(PRINT_MASK_ALL,
783			    usb_ah_log_handle, "usb_ah_check_usage_send_data:"
784			    "bad report: location=%d", bd->location);
785
786			continue;
787		}
788
789		ASSERT(ptr <= mp->b_wptr);
790
791		mask = ((1 << bd->no_of_bits) - 1);
792		val = (char)((*ptr >> bd->offset) & mask);
793
794		USB_DPRINTF_L4(PRINT_MASK_ALL,
795		    usb_ah_log_handle, "usb_ah_check_usage_send_data:"
796		    "usage=0x%x, "
797		    "mask=0x%x, val=0x%x", hid_rpt->usage_descr[i].usage_id,
798		    mask, val);
799
800		if (hid_rpt->usage_descr[i].collection_usage !=
801		    HID_CONSUMER_CONTROL) {
802			/*
803			 * skip item in unknown collections, for now.
804			 * this includes the volume and mute controls
805			 * in the microphone collection on plantronics
806			 * dsp-300 device with 3.xx firmware.
807			 */
808			continue;
809		}
810
811		switch (hid_rpt->usage_descr[i].usage_id) {
812		case HID_CONSUMER_VOL:	/* LC */
813			if (val != 0) {
814				if (hid_rpt->main_item_value &
815				    HID_MAIN_ITEM_RELATIVE) {
816					/* Relative volume */
817					mctlmsg.ioc_cmd = USB_AUDIO_VOL_CHANGE;
818					mctlmsg.ioc_count = sizeof (uint_t);
819					mctl_ptr = usba_mk_mctl(mctlmsg,
820					    &val, mctlmsg.ioc_count);
821					if (mctl_ptr != NULL) {
822						mutex_exit(&usb_ahd->
823						    usb_ah_mutex);
824						putnext(usb_ahd->usb_ah_readq,
825						    mctl_ptr);
826						mutex_enter(&usb_ahd->
827						    usb_ah_mutex);
828					}
829				} else {
830					USB_DPRINTF_L2(PRINT_MASK_ALL,
831					    usb_ah_log_handle, "usb_ah_rput:"
832					    "Absolute volume change "
833					    "not supported");
834				}
835			}
836
837			break;
838		case HID_CONSUMER_VOL_DECR: /* RTC */
839			if (val != 0) {
840				val = -val;
841			}
842			/* FALLTHRU */
843		case HID_CONSUMER_VOL_INCR:  /* RTC */
844			if (val != 0) {
845
846				/*
847				 * If another autorepeating button has been
848				 * pressed, cancel that one first
849				 */
850				usb_ah_cancel_timeout(usb_ahd);
851				mctlmsg.ioc_cmd = USB_AUDIO_VOL_CHANGE;
852				mctlmsg.ioc_count = sizeof (uint_t);
853				bd->pressed = 1;
854				usb_ah_repeat_send(usb_ahd, bd,
855				    mctlmsg, (char *)&val, mctlmsg.ioc_count);
856			} else {
857				/* Do not steal other's release event */
858				if (bd->pressed) {
859					usb_ah_cancel_timeout(usb_ahd);
860				}
861			}
862
863			break;
864		case HID_CONSUMER_MUTE:	/* OOC */
865			if (val) {
866				mctlmsg.ioc_cmd = USB_AUDIO_MUTE;
867				mctlmsg.ioc_count = sizeof (uint_t);
868				mctl_ptr = usba_mk_mctl(mctlmsg,
869				    &val, mctlmsg.ioc_count);
870				if (mctl_ptr != NULL) {
871					mutex_exit(&usb_ahd->usb_ah_mutex);
872					putnext(usb_ahd->usb_ah_readq,
873					    mctl_ptr);
874					mutex_enter(&usb_ahd->usb_ah_mutex);
875				}
876
877			}
878
879			break;
880		case HID_CONSUMER_BASS:
881		case HID_CONSUMER_TREBLE:
882		default:
883
884			break;
885		}
886	}
887	mutex_exit(&usb_ahd->usb_ah_mutex);
888	freemsg(mp);
889}
890