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 }