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
_init()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
_fini()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
_info(struct modinfo * modinfop)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
usb_ah_open(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * crp)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
usb_ah_close(register queue_t * q,int flag,cred_t * crp)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
usb_ah_rput(register queue_t * q,register mblk_t * mp)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
usb_ah_mctl_receive(register queue_t * q,register mblk_t * mp)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
usb_ah_repeat_send(usb_ah_state_t * usb_ahd,usb_ah_button_descr_t * bd,struct iocblk mctlmsg,char * buf,int len)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
usb_ah_timeout(void * addr)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
usb_ah_cancel_timeout(usb_ah_state_t * usb_ahd)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 *
usb_ah_cp_mblk(mblk_t * mp)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
usb_ah_get_cooked_rd(usb_ah_state_t * usb_ahd)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
usb_ah_check_usage_send_data(usb_ah_state_t * usb_ahd,mblk_t * mp)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 *
usb_ah_mk_mctl(struct iocblk mctlmsg,void * buf,size_t len)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