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