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