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