capi.c revision 452bca1bde500b8a6d830d232c2e3fe27cbe72b6
1/* 2 * 3 * Bluetooth packet analyzer - CAPI parser 4 * 5 * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 * 23 * $Id$ 24 */ 25 26#include <stdio.h> 27#include <errno.h> 28#include <ctype.h> 29#include <stdlib.h> 30#include <unistd.h> 31#include <string.h> 32 33#include <sys/types.h> 34#include <netinet/in.h> 35 36#include <bluetooth/bluetooth.h> 37 38#include "parser.h" 39 40#define CAPI_U8(frm) (get_u8(frm)) 41#define CAPI_U16(frm) (btohs(htons(get_u16(frm)))) 42#define CAPI_U32(frm) (btohl(htonl(get_u32(frm)))) 43 44static char *cmd2str(uint8_t cmd) 45{ 46 switch (cmd) { 47 case 0x01: 48 return "ALERT"; 49 case 0x02: 50 return "CONNECT"; 51 case 0x03: 52 return "CONNECT_ACTIVE"; 53 case 0x04: 54 return "DISCONNECT"; 55 case 0x05: 56 return "LISTEN"; 57 case 0x08: 58 return "INFO"; 59 case 0x20: 60 return "INTEROPERABILITY"; 61 case 0x41: 62 return "SELECT_B_PROTOCOL"; 63 case 0x80: 64 return "FACILITY"; 65 case 0x82: 66 return "CONNECT_B3"; 67 case 0x83: 68 return "CONNECT_B3_ACTIVE"; 69 case 0x84: 70 return "DISCONNECT_B3"; 71 case 0x86: 72 return "DATA_B3"; 73 case 0x87: 74 return "RESET_B3"; 75 case 0x88: 76 return "CONNECT_B3_T90_ACTIVE"; 77 case 0xff: 78 return "MANUFACTURER"; 79 default: 80 return "UNKNOWN"; 81 } 82} 83 84static char *subcmd2str(uint8_t subcmd) 85{ 86 switch (subcmd) { 87 case 0x80: 88 return "REQ"; 89 case 0x81: 90 return "CONF"; 91 case 0x82: 92 return "IND"; 93 case 0x83: 94 return "RESP"; 95 default: 96 return "UNKN"; 97 } 98} 99 100static char *interopsel2str(uint16_t sel) 101{ 102 switch (sel) { 103 case 0x0000: 104 return "USB Device Management"; 105 case 0x0001: 106 return "Bluetooth Device Management"; 107 default: 108 return "Unknown"; 109 } 110} 111 112static char *func2str(uint16_t func) 113{ 114 switch (func) { 115 case 0: 116 return "Register"; 117 case 1: 118 return "Release"; 119 case 2: 120 return "Get_Profile"; 121 case 3: 122 return "Get_Manufacturer"; 123 case 4: 124 return "Get_Version"; 125 case 5: 126 return "Get_Serial_Number"; 127 case 6: 128 return "Manufacturer"; 129 case 7: 130 return "Echo_Loopback"; 131 default: 132 return "Unknown"; 133 } 134} 135 136static char *facilitysel2str(uint16_t sel) 137{ 138 switch (sel) { 139 case 0x0000: 140 return "Handset"; 141 case 0x0001: 142 return "DTMF"; 143 case 0x0002: 144 return "V.42 bis"; 145 case 0x0003: 146 return "Supplementary Services"; 147 case 0x0004: 148 return "Power management wakeup"; 149 case 0x0005: 150 return "Line Interconnect"; 151 case 0x0006: 152 return "DTMF"; 153 default: 154 return "Unknown"; 155 } 156} 157 158static char *info2str(uint16_t info) 159{ 160 switch (info) { 161 case 0x0000: 162 return "No error"; 163 case 0x0001: 164 return "NCPI not supported by current protocol, NCPI ignored"; 165 case 0x0002: 166 return "Flags not supported by current protocol, flags ignored"; 167 case 0x2001: 168 return "Message not supported in current state"; 169 case 0x2002: 170 return "Incorrect Controller/PLCI/NCCI"; 171 case 0x2003: 172 return "No PLCI available"; 173 case 0x2004: 174 return "No NCCI available"; 175 case 0x2005: 176 return "No Listen resources available"; 177 case 0x2007: 178 return "Illegal message parameter coding"; 179 case 0x2008: 180 return "No interconnection resources available"; 181 case 0x3001: 182 return "B1 protocol not supported"; 183 case 0x3002: 184 return "B2 protocol not supported"; 185 case 0x3003: 186 return "B3 protocol not supported"; 187 case 0x3004: 188 return "B1 protocol parameter not supported"; 189 case 0x3005: 190 return "B2 protocol parameter not supported"; 191 case 0x3006: 192 return "B3 protocol parameter not supported"; 193 case 0x3007: 194 return "B protocol combination not supported"; 195 case 0x3008: 196 return "NCPI not supported"; 197 case 0x3009: 198 return "CIP Value unknown"; 199 case 0x300A: 200 return "Flags not supported (reserved bits)"; 201 case 0x300B: 202 return "Facility not supported"; 203 case 0x300C: 204 return "Data length not supported by current protocol"; 205 case 0x300D: 206 return "Reset procedure not supported by current protocol"; 207 case 0x300F: 208 return "Unsupported interoperability"; 209 case 0x3011: 210 return "Facility specific function not supported"; 211 case 0x3301: 212 return "Protocol error, Layer 1"; 213 case 0x3302: 214 return "Protocol error, Layer 2"; 215 case 0x3303: 216 return "Protocol error, Layer 3"; 217 case 0x3304: 218 return "Another application got that call"; 219 case 0x3305: 220 return "Cleared by Call Control Supervision"; 221 case 0x3400: 222 /* The cause value received from the network in a cause 223 * information element (Octet 4) is indicated in the field 00 */ 224 return "Disconnect cause from the network in accordance with Q.850/ETS 300 102-1"; 225 default: 226 return "Unknown"; 227 } 228} 229 230static void profile(int level, struct frame *frm) 231{ 232 uint16_t nctr, nchn; 233 uint32_t value; 234 235 nctr = CAPI_U16(frm); 236 nchn = CAPI_U16(frm); 237 238 if (nchn > 0) { 239 p_indent(level, frm); 240 printf("Controller: %d\n", nctr); 241 p_indent(level, frm); 242 printf("Number of B-channels: %d\n", nchn); 243 244 value = CAPI_U32(frm); 245 p_indent(level, frm); 246 printf("Global options: 0x%04x\n", value); 247 value = CAPI_U32(frm); 248 p_indent(level, frm); 249 printf("B1 protocol support: 0x%08x\n", value); 250 value = CAPI_U32(frm); 251 p_indent(level, frm); 252 printf("B2 protocol support: 0x%08x\n", value); 253 value = CAPI_U32(frm); 254 p_indent(level, frm); 255 printf("B3 protocol support: 0x%08x\n", value); 256 257 frm->ptr += 24; 258 frm->len -= 24; 259 260 p_indent(level, frm); 261 printf("Manufacturer-specific information:\n"); 262 hex_dump(level, frm, 20); 263 } else { 264 p_indent(level, frm); 265 printf("Number of controllers: %d\n", nctr); 266 } 267} 268 269static void cmd_common(int level, uint8_t subcmd, struct frame *frm) 270{ 271 uint32_t val; 272 uint16_t info, ncci; 273 uint8_t ctr, plci; 274 275 val = CAPI_U32(frm); 276 ctr = val & 0xff; 277 plci = (val & 0xff00) >> 8; 278 ncci = (val & 0xffff0000) >> 16; 279 280 p_indent(level, frm); 281 printf("Controller: %d %s\n", ctr & 0x7f, ctr & 0x80 ? "Ext." : "Int."); 282 283 if (plci > 0) { 284 p_indent(level, frm); 285 printf("PLCI: 0x%02x\n", plci); 286 } 287 288 if (ncci > 0) { 289 p_indent(level, frm); 290 printf("NCCI: 0x%04x\n", ncci); 291 } 292 293 if (subcmd == 0x81) { 294 info = CAPI_U16(frm); 295 p_indent(level, frm); 296 printf("Info: 0x%04x (%s)\n", info, info2str(info)); 297 } 298} 299 300static void cmd_alert(int level, uint8_t subcmd, struct frame *frm) 301{ 302 uint8_t len; 303 304 cmd_common(level, subcmd, frm); 305 306 if (subcmd == 0x80) { 307 len = CAPI_U8(frm); 308 if (len > 0) { 309 p_indent(level, frm); 310 printf("Additional info:\n"); 311 hex_dump(level, frm, len); 312 } 313 } 314} 315 316static void cmd_connect(int level, uint8_t subcmd, struct frame *frm) 317{ 318 uint16_t cip; 319 uint8_t len; 320 321 cmd_common(level, subcmd, frm); 322 323 if (subcmd == 0x81) 324 return; 325 326 cip = CAPI_U16(frm); 327 p_indent(level, frm); 328 printf("CIP value: 0x%04x\n", cip); 329 330 len = CAPI_U8(frm); 331 frm->ptr += len; 332 frm->len -= len; 333 len = CAPI_U8(frm); 334 frm->ptr += len; 335 frm->len -= len; 336 len = CAPI_U8(frm); 337 frm->ptr += len; 338 frm->len -= len; 339 len = CAPI_U8(frm); 340 frm->ptr += len; 341 frm->len -= len; 342 343 raw_dump(level, frm); 344} 345 346static void cmd_disconnect(int level, uint8_t subcmd, struct frame *frm) 347{ 348 uint16_t reason; 349 uint8_t len; 350 351 cmd_common(level, subcmd, frm); 352 353 if (subcmd == 0x80) { 354 len = CAPI_U8(frm); 355 if (len > 0) { 356 p_indent(level, frm); 357 printf("Additional info:\n"); 358 hex_dump(level, frm, len); 359 } 360 } 361 362 if (subcmd == 0x82) { 363 reason = CAPI_U16(frm); 364 p_indent(level, frm); 365 printf("Reason: 0x%04x (%s)\n", reason, info2str(reason)); 366 } 367} 368 369static void cmd_connect_active(int level, uint8_t subcmd, struct frame *frm) 370{ 371 uint8_t len; 372 373 cmd_common(level, subcmd, frm); 374 375 if (subcmd == 0x82) { 376 len = CAPI_U8(frm); 377 if (len > 0) { 378 p_indent(level, frm); 379 printf("Connected number:\n"); 380 hex_dump(level, frm, len); 381 } 382 383 len = CAPI_U8(frm); 384 if (len > 0) { 385 p_indent(level, frm); 386 printf("Connected subaddress:\n"); 387 hex_dump(level, frm, len); 388 } 389 390 len = CAPI_U8(frm); 391 if (len > 0) { 392 p_indent(level, frm); 393 printf("LLC:\n"); 394 hex_dump(level, frm, len); 395 } 396 } 397} 398 399static void cmd_listen(int level, uint8_t subcmd, struct frame *frm) 400{ 401 uint32_t mask; 402 uint8_t len; 403 404 cmd_common(level, subcmd, frm); 405 406 if (subcmd == 0x80) { 407 mask = CAPI_U32(frm); 408 p_indent(level, frm); 409 printf("Info mask: 0x%08x\n", mask); 410 411 mask = CAPI_U32(frm); 412 p_indent(level, frm); 413 printf("CIP mask: 0x%08x", mask); 414 415 mask = CAPI_U32(frm); 416 if (mask > 0) 417 printf(" 0x%08x\n", mask); 418 else 419 printf("\n"); 420 421 len = CAPI_U8(frm); 422 if (len > 0) { 423 p_indent(level, frm); 424 printf("Calling party number:\n"); 425 hex_dump(level, frm, len); 426 } 427 frm->ptr += len; 428 frm->len -= len; 429 430 len = CAPI_U8(frm); 431 if (len > 0) { 432 p_indent(level, frm); 433 printf("Calling party subaddress:\n"); 434 hex_dump(level, frm, len); 435 } 436 frm->ptr += len; 437 frm->len -= len; 438 } 439} 440 441static void cmd_info(int level, uint8_t subcmd, struct frame *frm) 442{ 443 uint8_t len; 444 uint16_t info; 445 446 cmd_common(level, subcmd, frm); 447 448 switch (subcmd) { 449 case 0x80: 450 len = CAPI_U8(frm); 451 if (len > 0) { 452 p_indent(level, frm); 453 printf("Called party number:\n"); 454 hex_dump(level, frm, len); 455 } 456 frm->ptr += len; 457 frm->len -= len; 458 459 len = CAPI_U8(frm); 460 if (len > 0) { 461 p_indent(level, frm); 462 printf("Additional info:\n"); 463 hex_dump(level, frm, len); 464 } 465 break; 466 467 case 0x82: 468 info = CAPI_U16(frm); 469 p_indent(level, frm); 470 printf("Info number: %d\n", info); 471 472 len = CAPI_U8(frm); 473 if (len > 0) { 474 p_indent(level, frm); 475 printf("Info element:\n"); 476 hex_dump(level, frm, len); 477 } 478 break; 479 } 480} 481 482static void cmd_interoperability(int level, uint8_t subcmd, struct frame *frm) 483{ 484 uint16_t sel, func, info; 485 uint16_t nconn, datablkcnt, datablklen; 486 uint32_t ctr, value, major, minor; 487 488 info = (subcmd == 0x81) ? CAPI_U16(frm) : 0; 489 sel = CAPI_U16(frm); 490 CAPI_U8(frm); 491 if (subcmd != 0x83) { 492 func = CAPI_U16(frm); 493 CAPI_U8(frm); 494 } else 495 func = 0; 496 497 p_indent(level, frm); 498 printf("Selector: 0x%04x (%s)\n", sel, interopsel2str(sel)); 499 500 switch (sel) { 501 case 0x0001: 502 p_indent(level, frm); 503 printf("Function: %d (%s)\n", func, func2str(func)); 504 505 switch (subcmd) { 506 case 0x80: 507 switch (func) { 508 case 0: 509 nconn = CAPI_U16(frm); 510 p_indent(level + 1, frm); 511 printf("maxLogicalConnections: %d\n", nconn); 512 datablkcnt = CAPI_U16(frm); 513 p_indent(level + 1, frm); 514 printf("maxBDataBlocks: %d\n", datablkcnt); 515 datablklen = CAPI_U16(frm); 516 p_indent(level + 1, frm); 517 printf("maxBDataLen: %d\n", datablklen); 518 break; 519 case 2: 520 case 3: 521 case 4: 522 case 5: 523 ctr = CAPI_U32(frm); 524 p_indent(level + 1, frm); 525 printf("Controller: %d\n", ctr); 526 break; 527 default: 528 raw_dump(level + 1, frm); 529 break; 530 } 531 break; 532 533 case 0x81: 534 switch (func) { 535 case 0: 536 case 1: 537 info = CAPI_U16(frm); 538 p_indent(level + 1, frm); 539 printf("Info: 0x%04x (%s)\n", info, info2str(info)); 540 break; 541 case 2: 542 info = CAPI_U16(frm); 543 p_indent(level + 1, frm); 544 printf("Info: 0x%04x (%s)\n", info, info2str(info)); 545 CAPI_U8(frm); 546 profile(level + 1, frm); 547 break; 548 case 3: 549 info = CAPI_U16(frm); 550 p_indent(level + 1, frm); 551 printf("Info: 0x%04x (%s)\n", info, info2str(info)); 552 ctr = CAPI_U32(frm); 553 p_indent(level + 1, frm); 554 printf("Controller: %d\n", ctr); 555 CAPI_U8(frm); 556 p_indent(level + 1, frm); 557 printf("Identification: \"%s\"\n", (char *) frm->ptr); 558 break; 559 case 4: 560 value = CAPI_U32(frm); 561 p_indent(level + 1, frm); 562 printf("Return value: 0x%04x\n", value); 563 ctr = CAPI_U32(frm); 564 p_indent(level + 1, frm); 565 printf("Controller: %d\n", ctr); 566 p_indent(level + 1, frm); 567 major = CAPI_U32(frm); 568 minor = CAPI_U32(frm); 569 printf("CAPI: %d.%d\n", major, minor); 570 major = CAPI_U32(frm); 571 minor = CAPI_U32(frm); 572 p_indent(level + 1, frm); 573 printf("Manufacture: %u.%01x%01x-%02u (%d.%d)\n", 574 (major & 0xf0) >> 4, (major & 0x0f) << 4, 575 (minor & 0xf0) >> 4, minor & 0x0f, 576 major, minor); 577 break; 578 case 5: 579 value = CAPI_U32(frm); 580 p_indent(level + 1, frm); 581 printf("Return value: 0x%04x\n", value); 582 ctr = CAPI_U32(frm); 583 p_indent(level + 1, frm); 584 printf("Controller: %d\n", ctr); 585 CAPI_U8(frm); 586 p_indent(level + 1, frm); 587 printf("Serial number: %.7s\n", (char *) frm->ptr); 588 break; 589 default: 590 raw_dump(level + 1, frm); 591 break; 592 } 593 break; 594 595 default: 596 raw_dump(level, frm); 597 break; 598 } 599 break; 600 601 default: 602 p_indent(level, frm); 603 printf("Function: %d\n", func); 604 if (subcmd == 0x81) { 605 p_indent(level, frm); 606 printf("Info: 0x%04x (%s)\n", info, info2str(info)); 607 } 608 raw_dump(level + 1, frm); 609 break; 610 } 611} 612 613static void cmd_facility(int level, uint8_t subcmd, struct frame *frm) 614{ 615 uint16_t sel; 616 617 cmd_common(level, subcmd, frm); 618 619 sel = CAPI_U16(frm); 620 CAPI_U8(frm); 621 622 p_indent(level, frm); 623 printf("Selector: 0x%04x (%s)\n", sel, facilitysel2str(sel)); 624 625 raw_dump(level, frm); 626} 627 628static void cmd_connect_b3(int level, uint8_t subcmd, struct frame *frm) 629{ 630 uint16_t reject; 631 uint8_t len; 632 633 cmd_common(level, subcmd, frm); 634 635 if (subcmd == 0x81) 636 return; 637 638 if (subcmd == 0x83) { 639 reject = CAPI_U16(frm); 640 p_indent(level, frm); 641 printf("Reject: 0x%04x (%s)\n", reject, info2str(reject)); 642 } 643 644 len = CAPI_U8(frm); 645 if (len > 0) { 646 p_indent(level, frm); 647 printf("NCPI:\n"); 648 hex_dump(level, frm, len); 649 } 650} 651 652static void cmd_connect_b3_active(int level, uint8_t subcmd, struct frame *frm) 653{ 654 uint8_t len; 655 656 cmd_common(level, subcmd, frm); 657 658 if (subcmd == 0x82) { 659 len = CAPI_U8(frm); 660 if (len > 0) { 661 p_indent(level, frm); 662 printf("NCPI:\n"); 663 hex_dump(level, frm, len); 664 } 665 } 666} 667 668static void cmd_disconnect_b3(int level, uint8_t subcmd, struct frame *frm) 669{ 670 uint16_t reason; 671 uint8_t len; 672 673 cmd_common(level, subcmd, frm); 674 675 if (subcmd == 0x82) { 676 reason = CAPI_U16(frm); 677 p_indent(level, frm); 678 printf("Reason: 0x%04x (%s)\n", reason, info2str(reason)); 679 } 680 681 if (subcmd == 0x80 || subcmd == 0x82) { 682 len = CAPI_U8(frm); 683 if (len > 0) { 684 p_indent(level, frm); 685 printf("NCPI:\n"); 686 hex_dump(level, frm, len); 687 } 688 } 689} 690 691static void cmd_data_b3(int level, uint8_t subcmd, struct frame *frm) 692{ 693 uint32_t data; 694 uint64_t data64; 695 uint16_t length, handle, flags, info; 696 697 cmd_common(level, 0x00, frm); 698 699 if (subcmd == 0x81 || subcmd == 0x83) { 700 handle = CAPI_U16(frm); 701 p_indent(level, frm); 702 printf("Data handle: 0x%04x\n", handle); 703 704 if (subcmd == 0x81) { 705 info = CAPI_U16(frm); 706 p_indent(level, frm); 707 printf("Info: 0x%04x (%s)\n", info, info2str(info)); 708 } 709 } else { 710 data = CAPI_U32(frm); 711 712 length = CAPI_U16(frm); 713 p_indent(level, frm); 714 printf("Data length: 0x%04x (%d bytes)\n", length, length); 715 716 handle = CAPI_U16(frm); 717 p_indent(level, frm); 718 printf("Data handle: 0x%04x\n", handle); 719 720 flags = CAPI_U16(frm); 721 p_indent(level, frm); 722 printf("Flags: 0x%04x\n", flags); 723 724 if (data == 0) 725 data64 = get_u64(frm); 726 727 raw_dump(level, frm); 728 } 729} 730 731static void cmd_reset_b3(int level, uint8_t subcmd, struct frame *frm) 732{ 733 uint8_t len; 734 735 cmd_common(level, subcmd, frm); 736 737 if (subcmd == 0x80 || subcmd == 0x82) { 738 len = CAPI_U8(frm); 739 if (len > 0) { 740 p_indent(level, frm); 741 printf("NCPI:\n"); 742 hex_dump(level, frm, len); 743 } 744 } 745} 746 747static void cmd_manufacturer(int level, uint8_t subcmd, struct frame *frm) 748{ 749 uint32_t ctr, class, func; 750 uint16_t len; 751 unsigned char *id; 752 753 ctr = CAPI_U32(frm); 754 p_indent(level, frm); 755 printf("Controller: %d\n", ctr); 756 757 id = (unsigned char *) frm->ptr; 758 p_indent(level, frm); 759 if (isprint(id[0]) && isprint(id[1]) && isprint(id[2]) && isprint(id[3])) 760 printf("Manufacturer: %.4s", id); 761 else 762 printf("Manufacturer: 0x%02x 0x%02x 0x%02x 0x%02x", 763 id[0], id[1], id[2], id[3]); 764 frm->ptr += 4; 765 frm->len -= 4; 766 767 if (!strncmp((char *) id, "AVM!", 4)) { 768 class = CAPI_U32(frm); 769 func = CAPI_U32(frm); 770 len = CAPI_U8(frm); 771 if (len == 0xff) 772 len = CAPI_U16(frm); 773 774 printf(" [class %d func %d len %d]\n", class, func, len); 775 } else 776 printf("\n"); 777 778 raw_dump(level, frm); 779} 780 781void capi_dump(int level, struct frame *frm) 782{ 783 uint16_t len, appl, msgnum; 784 uint8_t cmd, subcmd; 785 786 len = CAPI_U16(frm) - 8; 787 appl = CAPI_U16(frm); 788 cmd = CAPI_U8(frm); 789 subcmd = CAPI_U8(frm); 790 msgnum = CAPI_U16(frm); 791 792 p_indent(level, frm); 793 794 printf("CAPI_%s_%s: appl %d msgnum %d len %d\n", 795 cmd2str(cmd), subcmd2str(subcmd), appl, msgnum, len); 796 797 switch (cmd) { 798 case 0x01: 799 cmd_alert(level + 1, subcmd, frm); 800 break; 801 case 0x02: 802 cmd_connect(level + 1, subcmd, frm); 803 break; 804 case 0x03: 805 cmd_connect_active(level + 1, subcmd, frm); 806 break; 807 case 0x04: 808 cmd_disconnect(level + 1, subcmd, frm); 809 break; 810 case 0x05: 811 cmd_listen(level + 1, subcmd, frm); 812 break; 813 case 0x08: 814 cmd_info(level + 1, subcmd, frm); 815 break; 816 case 0x20: 817 cmd_interoperability(level + 1, subcmd, frm); 818 break; 819 case 0x80: 820 cmd_facility(level + 1, subcmd, frm); 821 break; 822 case 0x82: 823 cmd_connect_b3(level + 1, subcmd, frm); 824 break; 825 case 0x83: 826 case 0x88: 827 cmd_connect_b3_active(level + 1, subcmd, frm); 828 break; 829 case 0x84: 830 cmd_disconnect_b3(level + 1, subcmd, frm); 831 break; 832 case 0x86: 833 cmd_data_b3(level + 1, subcmd, frm); 834 break; 835 case 0x87: 836 cmd_reset_b3(level + 1, subcmd, frm); 837 break; 838 case 0xff: 839 cmd_manufacturer(level + 1, subcmd, frm); 840 break; 841 default: 842 raw_dump(level, frm); 843 frm->ptr += len; 844 frm->len -= len; 845 break; 846 } 847} 848