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 }