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