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