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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 * 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