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