1 /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ 2 3 4 #include <sys/cdefs.h> 5 __FBSDID("$FreeBSD$"); 6 /*- 7 * Copyright (c) 1998 The NetBSD Foundation, Inc. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to The NetBSD Foundation 11 * by Lennart Augustsson (lennart@augustsson.net) at 12 * Carlstedt Research & Technology. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. All advertising materials mentioning features or use of this software 23 * must display the following acknowledgement: 24 * This product includes software developed by the NetBSD 25 * Foundation, Inc. and its contributors. 26 * 4. Neither the name of The NetBSD Foundation nor the names of its 27 * contributors may be used to endorse or promote products derived 28 * from this software without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 32 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 34 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 * POSSIBILITY OF SUCH DAMAGE. 41 */ 42 43 #include <sys/stdint.h> 44 #include <sys/stddef.h> 45 #include <sys/param.h> 46 #include <sys/queue.h> 47 #include <sys/types.h> 48 #include <sys/systm.h> 49 #include <sys/kernel.h> 50 #include <sys/bus.h> 51 #include <sys/linker_set.h> 52 #include <sys/module.h> 53 #include <sys/lock.h> 54 #include <sys/mutex.h> 55 #include <sys/condvar.h> 56 #include <sys/sysctl.h> 57 #include <sys/sx.h> 58 #include <sys/unistd.h> 59 #include <sys/callout.h> 60 #include <sys/malloc.h> 61 #include <sys/priv.h> 62 63 #include <dev/usb/usb.h> 64 #include <dev/usb/usbdi.h> 65 #include <dev/usb/usbdi_util.h> 66 #include <dev/usb/usbhid.h> 67 68 #define USB_DEBUG_VAR usb_debug 69 70 #include <dev/usb/usb_core.h> 71 #include <dev/usb/usb_debug.h> 72 #include <dev/usb/usb_process.h> 73 #include <dev/usb/usb_device.h> 74 #include <dev/usb/usb_request.h> 75 76 static void hid_clear_local(struct hid_item *); 77 static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); 78 79 #define MAXUSAGE 64 80 #define MAXPUSH 4 81 #define MAXID 16 82 83 struct hid_pos_data { 84 int32_t rid; 85 uint32_t pos; 86 }; 87 88 struct hid_data { 89 const uint8_t *start; 90 const uint8_t *end; 91 const uint8_t *p; 92 struct hid_item cur[MAXPUSH]; 93 struct hid_pos_data last_pos[MAXID]; 94 int32_t usages_min[MAXUSAGE]; 95 int32_t usages_max[MAXUSAGE]; 96 int32_t usage_last; /* last seen usage */ 97 uint32_t loc_size; /* last seen size */ 98 uint32_t loc_count; /* last seen count */ 99 uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ 100 uint8_t pushlevel; /* current pushlevel */ 101 uint8_t ncount; /* end usage item count */ 102 uint8_t icount; /* current usage item count */ 103 uint8_t nusage; /* end "usages_min/max" index */ 104 uint8_t iusage; /* current "usages_min/max" index */ 105 uint8_t ousage; /* current "usages_min/max" offset */ 106 uint8_t susage; /* usage set flags */ 107 }; 108 109 /*------------------------------------------------------------------------* 110 * hid_clear_local 111 *------------------------------------------------------------------------*/ 112 static void 113 hid_clear_local(struct hid_item *c) 114 { 115 116 c->loc.count = 0; 117 c->loc.size = 0; 118 c->usage = 0; 119 c->usage_minimum = 0; 120 c->usage_maximum = 0; 121 c->designator_index = 0; 122 c->designator_minimum = 0; 123 c->designator_maximum = 0; 124 c->string_index = 0; 125 c->string_minimum = 0; 126 c->string_maximum = 0; 127 c->set_delimiter = 0; 128 } 129 130 static void 131 hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) 132 { 133 uint8_t i; 134 135 /* check for same report ID - optimise */ 136 137 if (c->report_ID == next_rID) 138 return; 139 140 /* save current position for current rID */ 141 142 if (c->report_ID == 0) { 143 i = 0; 144 } else { 145 for (i = 1; i != MAXID; i++) { 146 if (s->last_pos[i].rid == c->report_ID) 147 break; 148 if (s->last_pos[i].rid == 0) 149 break; 150 } 151 } 152 if (i != MAXID) { 153 s->last_pos[i].rid = c->report_ID; 154 s->last_pos[i].pos = c->loc.pos; 155 } 156 157 /* store next report ID */ 158 159 c->report_ID = next_rID; 160 161 /* lookup last position for next rID */ 162 163 if (next_rID == 0) { 164 i = 0; 165 } else { 166 for (i = 1; i != MAXID; i++) { 167 if (s->last_pos[i].rid == next_rID) 168 break; 169 if (s->last_pos[i].rid == 0) 170 break; 171 } 172 } 173 if (i != MAXID) { 174 s->last_pos[i].rid = next_rID; 175 c->loc.pos = s->last_pos[i].pos; 176 } else { 177 DPRINTF("Out of RID entries, position is set to zero!\n"); 178 c->loc.pos = 0; 179 } 180 } 181 182 /*------------------------------------------------------------------------* 183 * hid_start_parse 184 *------------------------------------------------------------------------*/ 185 struct hid_data * 186 hid_start_parse(const void *d, usb_size_t len, int kindset) 187 { 188 struct hid_data *s; 189 190 if ((kindset-1) & kindset) { 191 DPRINTFN(0, "Only one bit can be " 192 "set in the kindset\n"); 193 return (NULL); 194 } 195 196 s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); 197 s->start = s->p = d; 198 s->end = ((const uint8_t *)d) + len; 199 s->kindset = kindset; 200 return (s); 201 } 202 203 /*------------------------------------------------------------------------* 204 * hid_end_parse 205 *------------------------------------------------------------------------*/ 206 void 207 hid_end_parse(struct hid_data *s) 208 { 209 if (s == NULL) 210 return; 211 212 free(s, M_TEMP); 213 } 214 215 /*------------------------------------------------------------------------* 216 * get byte from HID descriptor 217 *------------------------------------------------------------------------*/ 218 static uint8_t 219 hid_get_byte(struct hid_data *s, const uint16_t wSize) 220 { 221 const uint8_t *ptr; 222 uint8_t retval; 223 224 ptr = s->p; 225 226 /* check if end is reached */ 227 if (ptr == s->end) 228 return (0); 229 230 /* read out a byte */ 231 retval = *ptr; 232 233 /* check if data pointer can be advanced by "wSize" bytes */ 234 if ((s->end - ptr) < wSize) 235 ptr = s->end; 236 else 237 ptr += wSize; 238 239 /* update pointer */ 240 s->p = ptr; 241 242 return (retval); 243 } 244 245 /*------------------------------------------------------------------------* 246 * hid_get_item 247 *------------------------------------------------------------------------*/ 248 int 249 hid_get_item(struct hid_data *s, struct hid_item *h) 250 { 251 struct hid_item *c; 252 unsigned int bTag, bType, bSize; 253 uint32_t oldpos; 254 int32_t mask; 255 int32_t dval; 256 257 if (s == NULL) 258 return (0); 259 260 c = &s->cur[s->pushlevel]; 261 262 top: 263 /* check if there is an array of items */ 264 if (s->icount < s->ncount) { 265 /* get current usage */ 266 if (s->iusage < s->nusage) { 267 dval = s->usages_min[s->iusage] + s->ousage; 268 c->usage = dval; 269 s->usage_last = dval; 270 if (dval == s->usages_max[s->iusage]) { 271 s->iusage ++; 272 s->ousage = 0; 273 } else { 274 s->ousage ++; 275 } 276 } else { 277 DPRINTFN(1, "Using last usage\n"); 278 dval = s->usage_last; 279 } 280 s->icount ++; 281 /* 282 * Only copy HID item, increment position and return 283 * if correct kindset! 284 */ 285 if (s->kindset & (1 << c->kind)) { 286 *h = *c; 287 DPRINTFN(1, "%u,%u,%u\n", h->loc.pos, 288 h->loc.size, h->loc.count); 289 c->loc.pos += c->loc.size * c->loc.count; 290 return (1); 291 } 292 } 293 294 /* reset state variables */ 295 s->icount = 0; 296 s->ncount = 0; 297 s->iusage = 0; 298 s->nusage = 0; 299 s->susage = 0; 300 s->ousage = 0; 301 hid_clear_local(c); 302 303 /* get next item */ 304 while (s->p != s->end) { 305 306 bSize = hid_get_byte(s, 1); 307 if (bSize == 0xfe) { 308 /* long item */ 309 bSize = hid_get_byte(s, 1); 310 bSize |= hid_get_byte(s, 1) << 8; 311 bTag = hid_get_byte(s, 1); 312 bType = 0xff; /* XXX what should it be */ 313 } else { 314 /* short item */ 315 bTag = bSize >> 4; 316 bType = (bSize >> 2) & 3; 317 bSize &= 3; 318 if (bSize == 3) 319 bSize = 4; 320 } 321 switch (bSize) { 322 case 0: 323 dval = 0; 324 mask = 0; 325 break; 326 case 1: 327 dval = (int8_t)hid_get_byte(s, 1); 328 mask = 0xFF; 329 break; 330 case 2: 331 dval = hid_get_byte(s, 1); 332 dval |= hid_get_byte(s, 1) << 8; 333 dval = (int16_t)dval; 334 mask = 0xFFFF; 335 break; 336 case 4: 337 dval = hid_get_byte(s, 1); 338 dval |= hid_get_byte(s, 1) << 8; 339 dval |= hid_get_byte(s, 1) << 16; 340 dval |= hid_get_byte(s, 1) << 24; 341 mask = 0xFFFFFFFF; 342 break; 343 default: 344 dval = hid_get_byte(s, bSize); 345 DPRINTFN(0, "bad length %u (data=0x%02x)\n", 346 bSize, dval); 347 continue; 348 } 349 350 switch (bType) { 351 case 0: /* Main */ 352 switch (bTag) { 353 case 8: /* Input */ 354 c->kind = hid_input; 355 c->flags = dval; 356 ret: 357 c->loc.count = s->loc_count; 358 c->loc.size = s->loc_size; 359 360 if (c->flags & HIO_VARIABLE) { 361 /* range check usage count */ 362 if (c->loc.count > 255) { 363 DPRINTFN(0, "Number of " 364 "items truncated to 255\n"); 365 s->ncount = 255; 366 } else 367 s->ncount = c->loc.count; 368 369 /* 370 * The "top" loop will return 371 * one and one item: 372 */ 373 c->loc.count = 1; 374 } else { 375 s->ncount = 1; 376 } 377 goto top; 378 379 case 9: /* Output */ 380 c->kind = hid_output; 381 c->flags = dval; 382 goto ret; 383 case 10: /* Collection */ 384 c->kind = hid_collection; 385 c->collection = dval; 386 c->collevel++; 387 c->usage = s->usage_last; 388 *h = *c; 389 return (1); 390 case 11: /* Feature */ 391 c->kind = hid_feature; 392 c->flags = dval; 393 goto ret; 394 case 12: /* End collection */ 395 c->kind = hid_endcollection; 396 if (c->collevel == 0) { 397 DPRINTFN(0, "invalid end collection\n"); 398 return (0); 399 } 400 c->collevel--; 401 *h = *c; 402 return (1); 403 default: 404 DPRINTFN(0, "Main bTag=%d\n", bTag); 405 break; 406 } 407 break; 408 case 1: /* Global */ 409 switch (bTag) { 410 case 0: 411 c->_usage_page = dval << 16; 412 break; 413 case 1: 414 c->logical_minimum = dval; 415 break; 416 case 2: 417 c->logical_maximum = dval; 418 break; 419 case 3: 420 c->physical_minimum = dval; 421 break; 422 case 4: 423 c->physical_maximum = dval; 424 break; 425 case 5: 426 c->unit_exponent = dval; 427 break; 428 case 6: 429 c->unit = dval; 430 break; 431 case 7: 432 /* mask because value is unsigned */ 433 s->loc_size = dval & mask; 434 break; 435 case 8: 436 hid_switch_rid(s, c, dval); 437 break; 438 case 9: 439 /* mask because value is unsigned */ 440 s->loc_count = dval & mask; 441 break; 442 case 10: /* Push */ 443 s->pushlevel ++; 444 if (s->pushlevel < MAXPUSH) { 445 s->cur[s->pushlevel] = *c; 446 /* store size and count */ 447 c->loc.size = s->loc_size; 448 c->loc.count = s->loc_count; 449 /* update current item pointer */ 450 c = &s->cur[s->pushlevel]; 451 } else { 452 DPRINTFN(0, "Cannot push " 453 "item @ %d!\n", s->pushlevel); 454 } 455 break; 456 case 11: /* Pop */ 457 s->pushlevel --; 458 if (s->pushlevel < MAXPUSH) { 459 /* preserve position */ 460 oldpos = c->loc.pos; 461 c = &s->cur[s->pushlevel]; 462 /* restore size and count */ 463 s->loc_size = c->loc.size; 464 s->loc_count = c->loc.count; 465 /* set default item location */ 466 c->loc.pos = oldpos; 467 c->loc.size = 0; 468 c->loc.count = 0; 469 } else { 470 DPRINTFN(0, "Cannot pop " 471 "item @ %d!\n", s->pushlevel); 472 } 473 break; 474 default: 475 DPRINTFN(0, "Global bTag=%d\n", bTag); 476 break; 477 } 478 break; 479 case 2: /* Local */ 480 switch (bTag) { 481 case 0: 482 if (bSize != 4) 483 dval = (dval & mask) | c->_usage_page; 484 485 /* set last usage, in case of a collection */ 486 s->usage_last = dval; 487 488 if (s->nusage < MAXUSAGE) { 489 s->usages_min[s->nusage] = dval; 490 s->usages_max[s->nusage] = dval; 491 s->nusage ++; 492 } else { 493 DPRINTFN(0, "max usage reached!\n"); 494 } 495 496 /* clear any pending usage sets */ 497 s->susage = 0; 498 break; 499 case 1: 500 s->susage |= 1; 501 502 if (bSize != 4) 503 dval = (dval & mask) | c->_usage_page; 504 c->usage_minimum = dval; 505 506 goto check_set; 507 case 2: 508 s->susage |= 2; 509 510 if (bSize != 4) 511 dval = (dval & mask) | c->_usage_page; 512 c->usage_maximum = dval; 513 514 check_set: 515 if (s->susage != 3) 516 break; 517 518 /* sanity check */ 519 if ((s->nusage < MAXUSAGE) && 520 (c->usage_minimum <= c->usage_maximum)) { 521 /* add usage range */ 522 s->usages_min[s->nusage] = 523 c->usage_minimum; 524 s->usages_max[s->nusage] = 525 c->usage_maximum; 526 s->nusage ++; 527 } else { 528 DPRINTFN(0, "Usage set dropped!\n"); 529 } 530 s->susage = 0; 531 break; 532 case 3: 533 c->designator_index = dval; 534 break; 535 case 4: 536 c->designator_minimum = dval; 537 break; 538 case 5: 539 c->designator_maximum = dval; 540 break; 541 case 7: 542 c->string_index = dval; 543 break; 544 case 8: 545 c->string_minimum = dval; 546 break; 547 case 9: 548 c->string_maximum = dval; 549 break; 550 case 10: 551 c->set_delimiter = dval; 552 break; 553 default: 554 DPRINTFN(0, "Local bTag=%d\n", bTag); 555 break; 556 } 557 break; 558 default: 559 DPRINTFN(0, "default bType=%d\n", bType); 560 break; 561 } 562 } 563 return (0); 564 } 565 566 /*------------------------------------------------------------------------* 567 * hid_report_size 568 *------------------------------------------------------------------------*/ 569 int 570 hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) 571 { 572 struct hid_data *d; 573 struct hid_item h; 574 uint32_t temp; 575 uint32_t hpos; 576 uint32_t lpos; 577 uint8_t any_id; 578 579 any_id = 0; 580 hpos = 0; 581 lpos = 0xFFFFFFFF; 582 583 for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { 584 if (h.kind == k) { 585 /* check for ID-byte presense */ 586 if ((h.report_ID != 0) && !any_id) { 587 if (id != NULL) 588 *id = h.report_ID; 589 any_id = 1; 590 } 591 /* compute minimum */ 592 if (lpos > h.loc.pos) 593 lpos = h.loc.pos; 594 /* compute end position */ 595 temp = h.loc.pos + (h.loc.size * h.loc.count); 596 /* compute maximum */ 597 if (hpos < temp) 598 hpos = temp; 599 } 600 } 601 hid_end_parse(d); 602 603 /* safety check - can happen in case of currupt descriptors */ 604 if (lpos > hpos) 605 temp = 0; 606 else 607 temp = hpos - lpos; 608 609 /* check for ID byte */ 610 if (any_id) 611 temp += 8; 612 else if (id != NULL) 613 *id = 0; 614 615 /* return length in bytes rounded up */ 616 return ((temp + 7) / 8); 617 } 618 619 /*------------------------------------------------------------------------* 620 * hid_locate 621 *------------------------------------------------------------------------*/ 622 int 623 hid_locate(const void *desc, usb_size_t size, uint32_t u, enum hid_kind k, 624 uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) 625 { 626 struct hid_data *d; 627 struct hid_item h; 628 629 for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { 630 if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { 631 if (index--) 632 continue; 633 if (loc != NULL) 634 *loc = h.loc; 635 if (flags != NULL) 636 *flags = h.flags; 637 if (id != NULL) 638 *id = h.report_ID; 639 hid_end_parse(d); 640 return (1); 641 } 642 } 643 if (loc != NULL) 644 loc->size = 0; 645 if (flags != NULL) 646 *flags = 0; 647 if (id != NULL) 648 *id = 0; 649 hid_end_parse(d); 650 return (0); 651 } 652 653 /*------------------------------------------------------------------------* 654 * hid_get_data 655 *------------------------------------------------------------------------*/ 656 uint32_t 657 hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) 658 { 659 uint32_t hpos = loc->pos; 660 uint32_t hsize = loc->size; 661 uint32_t data; 662 uint32_t rpos; 663 uint8_t n; 664 665 DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize); 666 667 /* Range check and limit */ 668 if (hsize == 0) 669 return (0); 670 if (hsize > 32) 671 hsize = 32; 672 673 /* Get data in a safe way */ 674 data = 0; 675 rpos = (hpos / 8); 676 n = (hsize + 7) / 8; 677 rpos += n; 678 while (n--) { 679 rpos--; 680 if (rpos < len) 681 data |= buf[rpos] << (8 * n); 682 } 683 684 /* Correctly shift down data */ 685 data = (data >> (hpos % 8)); 686 687 /* Mask and sign extend in one */ 688 n = 32 - hsize; 689 data = ((int32_t)data << n) >> n; 690 691 DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", 692 loc->pos, loc->size, (long)data); 693 return (data); 694 } 695 696 /*------------------------------------------------------------------------* 697 * hid_is_collection 698 *------------------------------------------------------------------------*/ 699 int 700 hid_is_collection(const void *desc, usb_size_t size, uint32_t usage) 701 { 702 struct hid_data *hd; 703 struct hid_item hi; 704 int err; 705 706 hd = hid_start_parse(desc, size, hid_input); 707 if (hd == NULL) 708 return (0); 709 710 while ((err = hid_get_item(hd, &hi))) { 711 if (hi.kind == hid_collection && 712 hi.usage == usage) 713 break; 714 } 715 hid_end_parse(hd); 716 return (err); 717 } 718 719 /*------------------------------------------------------------------------* 720 * hid_get_descriptor_from_usb 721 * 722 * This function will search for a HID descriptor between two USB 723 * interface descriptors. 724 * 725 * Return values: 726 * NULL: No more HID descriptors. 727 * Else: Pointer to HID descriptor. 728 *------------------------------------------------------------------------*/ 729 struct usb_hid_descriptor * 730 hid_get_descriptor_from_usb(struct usb_config_descriptor *cd, 731 struct usb_interface_descriptor *id) 732 { 733 struct usb_descriptor *desc = (void *)id; 734 735 if (desc == NULL) { 736 return (NULL); 737 } 738 while ((desc = usb_desc_foreach(cd, desc))) { 739 if ((desc->bDescriptorType == UDESC_HID) && 740 (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { 741 return (void *)desc; 742 } 743 if (desc->bDescriptorType == UDESC_INTERFACE) { 744 break; 745 } 746 } 747 return (NULL); 748 } 749 750 /*------------------------------------------------------------------------* 751 * usbd_req_get_hid_desc 752 * 753 * This function will read out an USB report descriptor from the USB 754 * device. 755 * 756 * Return values: 757 * NULL: Failure. 758 * Else: Success. The pointer should eventually be passed to free(). 759 *------------------------------------------------------------------------*/ 760 usb_error_t 761 usbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, 762 void **descp, uint16_t *sizep, 763 struct malloc_type *mem, uint8_t iface_index) 764 { 765 struct usb_interface *iface = usbd_get_iface(udev, iface_index); 766 struct usb_hid_descriptor *hid; 767 usb_error_t err; 768 769 if ((iface == NULL) || (iface->idesc == NULL)) { 770 return (USB_ERR_INVAL); 771 } 772 hid = hid_get_descriptor_from_usb 773 (usbd_get_config_descriptor(udev), iface->idesc); 774 775 if (hid == NULL) { 776 return (USB_ERR_IOERROR); 777 } 778 *sizep = UGETW(hid->descrs[0].wDescriptorLength); 779 if (*sizep == 0) { 780 return (USB_ERR_IOERROR); 781 } 782 if (mtx) 783 mtx_unlock(mtx); 784 785 *descp = malloc(*sizep, mem, M_ZERO | M_WAITOK); 786 787 if (mtx) 788 mtx_lock(mtx); 789 790 if (*descp == NULL) { 791 return (USB_ERR_NOMEM); 792 } 793 err = usbd_req_get_report_descriptor 794 (udev, mtx, *descp, *sizep, iface_index); 795 796 if (err) { 797 free(*descp, mem); 798 *descp = NULL; 799 return (err); 800 } 801 return (USB_ERR_NORMAL_COMPLETION); 802 } 803