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