| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> |
| * |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "parser.h" |
| |
| #define CAPI_U8(frm) (get_u8(frm)) |
| #define CAPI_U16(frm) (btohs(htons(get_u16(frm)))) |
| #define CAPI_U32(frm) (btohl(htonl(get_u32(frm)))) |
| |
| static char *cmd2str(uint8_t cmd) |
| { |
| switch (cmd) { |
| case 0x01: |
| return "ALERT"; |
| case 0x02: |
| return "CONNECT"; |
| case 0x03: |
| return "CONNECT_ACTIVE"; |
| case 0x04: |
| return "DISCONNECT"; |
| case 0x05: |
| return "LISTEN"; |
| case 0x08: |
| return "INFO"; |
| case 0x20: |
| return "INTEROPERABILITY"; |
| case 0x41: |
| return "SELECT_B_PROTOCOL"; |
| case 0x80: |
| return "FACILITY"; |
| case 0x82: |
| return "CONNECT_B3"; |
| case 0x83: |
| return "CONNECT_B3_ACTIVE"; |
| case 0x84: |
| return "DISCONNECT_B3"; |
| case 0x86: |
| return "DATA_B3"; |
| case 0x87: |
| return "RESET_B3"; |
| case 0x88: |
| return "CONNECT_B3_T90_ACTIVE"; |
| case 0xff: |
| return "MANUFACTURER"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static char *subcmd2str(uint8_t subcmd) |
| { |
| switch (subcmd) { |
| case 0x80: |
| return "REQ"; |
| case 0x81: |
| return "CONF"; |
| case 0x82: |
| return "IND"; |
| case 0x83: |
| return "RESP"; |
| default: |
| return "UNKN"; |
| } |
| } |
| |
| static char *interopsel2str(uint16_t sel) |
| { |
| switch (sel) { |
| case 0x0000: |
| return "USB Device Management"; |
| case 0x0001: |
| return "Bluetooth Device Management"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| static char *func2str(uint16_t func) |
| { |
| switch (func) { |
| case 0: |
| return "Register"; |
| case 1: |
| return "Release"; |
| case 2: |
| return "Get_Profile"; |
| case 3: |
| return "Get_Manufacturer"; |
| case 4: |
| return "Get_Version"; |
| case 5: |
| return "Get_Serial_Number"; |
| case 6: |
| return "Manufacturer"; |
| case 7: |
| return "Echo_Loopback"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| static char *facilitysel2str(uint16_t sel) |
| { |
| switch (sel) { |
| case 0x0000: |
| return "Handset"; |
| case 0x0001: |
| return "DTMF"; |
| case 0x0002: |
| return "V.42 bis"; |
| case 0x0003: |
| return "Supplementary Services"; |
| case 0x0004: |
| return "Power management wakeup"; |
| case 0x0005: |
| return "Line Interconnect"; |
| case 0x0006: |
| return "DTMF"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| static char *info2str(uint16_t info) |
| { |
| switch (info) { |
| case 0x0000: |
| return "No error"; |
| case 0x0001: |
| return "NCPI not supported by current protocol, NCPI ignored"; |
| case 0x0002: |
| return "Flags not supported by current protocol, flags ignored"; |
| case 0x2001: |
| return "Message not supported in current state"; |
| case 0x2002: |
| return "Incorrect Controller/PLCI/NCCI"; |
| case 0x2003: |
| return "No PLCI available"; |
| case 0x2004: |
| return "No NCCI available"; |
| case 0x2005: |
| return "No Listen resources available"; |
| case 0x2007: |
| return "Illegal message parameter coding"; |
| case 0x2008: |
| return "No interconnection resources available"; |
| case 0x3001: |
| return "B1 protocol not supported"; |
| case 0x3002: |
| return "B2 protocol not supported"; |
| case 0x3003: |
| return "B3 protocol not supported"; |
| case 0x3004: |
| return "B1 protocol parameter not supported"; |
| case 0x3005: |
| return "B2 protocol parameter not supported"; |
| case 0x3006: |
| return "B3 protocol parameter not supported"; |
| case 0x3007: |
| return "B protocol combination not supported"; |
| case 0x3008: |
| return "NCPI not supported"; |
| case 0x3009: |
| return "CIP Value unknown"; |
| case 0x300A: |
| return "Flags not supported (reserved bits)"; |
| case 0x300B: |
| return "Facility not supported"; |
| case 0x300C: |
| return "Data length not supported by current protocol"; |
| case 0x300D: |
| return "Reset procedure not supported by current protocol"; |
| case 0x300F: |
| return "Unsupported interoperability"; |
| case 0x3011: |
| return "Facility specific function not supported"; |
| case 0x3301: |
| return "Protocol error, Layer 1"; |
| case 0x3302: |
| return "Protocol error, Layer 2"; |
| case 0x3303: |
| return "Protocol error, Layer 3"; |
| case 0x3304: |
| return "Another application got that call"; |
| case 0x3305: |
| return "Cleared by Call Control Supervision"; |
| case 0x3400: |
| /* The cause value received from the network in a cause |
| * information element (Octet 4) is indicated in the field 00 */ |
| return "Disconnect cause from the network in accordance with Q.850/ETS 300 102-1"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| static void profile(int level, struct frame *frm) |
| { |
| uint16_t nctr, nchn; |
| uint32_t value; |
| |
| nctr = CAPI_U16(frm); |
| nchn = CAPI_U16(frm); |
| |
| if (nchn > 0) { |
| p_indent(level, frm); |
| printf("Controller: %d\n", nctr); |
| p_indent(level, frm); |
| printf("Number of B-channels: %d\n", nchn); |
| |
| value = CAPI_U32(frm); |
| p_indent(level, frm); |
| printf("Global options: 0x%04x\n", value); |
| value = CAPI_U32(frm); |
| p_indent(level, frm); |
| printf("B1 protocol support: 0x%08x\n", value); |
| value = CAPI_U32(frm); |
| p_indent(level, frm); |
| printf("B2 protocol support: 0x%08x\n", value); |
| value = CAPI_U32(frm); |
| p_indent(level, frm); |
| printf("B3 protocol support: 0x%08x\n", value); |
| |
| frm->ptr += 24; |
| frm->len -= 24; |
| |
| p_indent(level, frm); |
| printf("Manufacturer-specific information:\n"); |
| hex_dump(level, frm, 20); |
| } else { |
| p_indent(level, frm); |
| printf("Number of controllers: %d\n", nctr); |
| } |
| } |
| |
| static void cmd_common(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint32_t val; |
| uint16_t info, ncci; |
| uint8_t ctr, plci; |
| |
| val = CAPI_U32(frm); |
| ctr = val & 0xff; |
| plci = (val & 0xff00) >> 8; |
| ncci = (val & 0xffff0000) >> 16; |
| |
| p_indent(level, frm); |
| printf("Controller: %d %s\n", ctr & 0x7f, ctr & 0x80 ? "Ext." : "Int."); |
| |
| if (plci > 0) { |
| p_indent(level, frm); |
| printf("PLCI: 0x%02x\n", plci); |
| } |
| |
| if (ncci > 0) { |
| p_indent(level, frm); |
| printf("NCCI: 0x%04x\n", ncci); |
| } |
| |
| if (subcmd == 0x81) { |
| info = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Info: 0x%04x (%s)\n", info, info2str(info)); |
| } |
| } |
| |
| static void cmd_alert(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x80) { |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Additional info:\n"); |
| hex_dump(level, frm, len); |
| } |
| } |
| } |
| |
| static void cmd_connect(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint16_t cip; |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x81) |
| return; |
| |
| cip = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("CIP value: 0x%04x\n", cip); |
| |
| len = CAPI_U8(frm); |
| frm->ptr += len; |
| frm->len -= len; |
| len = CAPI_U8(frm); |
| frm->ptr += len; |
| frm->len -= len; |
| len = CAPI_U8(frm); |
| frm->ptr += len; |
| frm->len -= len; |
| len = CAPI_U8(frm); |
| frm->ptr += len; |
| frm->len -= len; |
| |
| raw_dump(level, frm); |
| } |
| |
| static void cmd_disconnect(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint16_t reason; |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x80) { |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Additional info:\n"); |
| hex_dump(level, frm, len); |
| } |
| } |
| |
| if (subcmd == 0x82) { |
| reason = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Reason: 0x%04x (%s)\n", reason, info2str(reason)); |
| } |
| } |
| |
| static void cmd_connect_active(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x82) { |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Connected number:\n"); |
| hex_dump(level, frm, len); |
| } |
| |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Connected subaddress:\n"); |
| hex_dump(level, frm, len); |
| } |
| |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("LLC:\n"); |
| hex_dump(level, frm, len); |
| } |
| } |
| } |
| |
| static void cmd_listen(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint32_t mask; |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x80) { |
| mask = CAPI_U32(frm); |
| p_indent(level, frm); |
| printf("Info mask: 0x%08x\n", mask); |
| |
| mask = CAPI_U32(frm); |
| p_indent(level, frm); |
| printf("CIP mask: 0x%08x", mask); |
| |
| mask = CAPI_U32(frm); |
| if (mask > 0) |
| printf(" 0x%08x\n", mask); |
| else |
| printf("\n"); |
| |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Calling party number:\n"); |
| hex_dump(level, frm, len); |
| } |
| frm->ptr += len; |
| frm->len -= len; |
| |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Calling party subaddress:\n"); |
| hex_dump(level, frm, len); |
| } |
| frm->ptr += len; |
| frm->len -= len; |
| } |
| } |
| |
| static void cmd_info(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint8_t len; |
| uint16_t info; |
| |
| cmd_common(level, subcmd, frm); |
| |
| switch (subcmd) { |
| case 0x80: |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Called party number:\n"); |
| hex_dump(level, frm, len); |
| } |
| frm->ptr += len; |
| frm->len -= len; |
| |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Additional info:\n"); |
| hex_dump(level, frm, len); |
| } |
| break; |
| |
| case 0x82: |
| info = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Info number: %d\n", info); |
| |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("Info element:\n"); |
| hex_dump(level, frm, len); |
| } |
| break; |
| } |
| } |
| |
| static void cmd_interoperability(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint16_t sel, func, info; |
| uint16_t nconn, datablkcnt, datablklen; |
| uint32_t ctr, value, major, minor; |
| |
| info = (subcmd == 0x81) ? CAPI_U16(frm) : 0; |
| sel = CAPI_U16(frm); |
| CAPI_U8(frm); |
| if (subcmd != 0x83) { |
| func = CAPI_U16(frm); |
| CAPI_U8(frm); |
| } else |
| func = 0; |
| |
| p_indent(level, frm); |
| printf("Selector: 0x%04x (%s)\n", sel, interopsel2str(sel)); |
| |
| switch (sel) { |
| case 0x0001: |
| p_indent(level, frm); |
| printf("Function: %d (%s)\n", func, func2str(func)); |
| |
| switch (subcmd) { |
| case 0x80: |
| switch (func) { |
| case 0: |
| nconn = CAPI_U16(frm); |
| p_indent(level + 1, frm); |
| printf("maxLogicalConnections: %d\n", nconn); |
| datablkcnt = CAPI_U16(frm); |
| p_indent(level + 1, frm); |
| printf("maxBDataBlocks: %d\n", datablkcnt); |
| datablklen = CAPI_U16(frm); |
| p_indent(level + 1, frm); |
| printf("maxBDataLen: %d\n", datablklen); |
| break; |
| case 2: |
| case 3: |
| case 4: |
| case 5: |
| ctr = CAPI_U32(frm); |
| p_indent(level + 1, frm); |
| printf("Controller: %d\n", ctr); |
| break; |
| default: |
| raw_dump(level + 1, frm); |
| break; |
| } |
| break; |
| |
| case 0x81: |
| switch (func) { |
| case 0: |
| case 1: |
| info = CAPI_U16(frm); |
| p_indent(level + 1, frm); |
| printf("Info: 0x%04x (%s)\n", info, info2str(info)); |
| break; |
| case 2: |
| info = CAPI_U16(frm); |
| p_indent(level + 1, frm); |
| printf("Info: 0x%04x (%s)\n", info, info2str(info)); |
| CAPI_U8(frm); |
| profile(level + 1, frm); |
| break; |
| case 3: |
| info = CAPI_U16(frm); |
| p_indent(level + 1, frm); |
| printf("Info: 0x%04x (%s)\n", info, info2str(info)); |
| ctr = CAPI_U32(frm); |
| p_indent(level + 1, frm); |
| printf("Controller: %d\n", ctr); |
| CAPI_U8(frm); |
| p_indent(level + 1, frm); |
| printf("Identification: \"%s\"\n", (char *) frm->ptr); |
| break; |
| case 4: |
| value = CAPI_U32(frm); |
| p_indent(level + 1, frm); |
| printf("Return value: 0x%04x\n", value); |
| ctr = CAPI_U32(frm); |
| p_indent(level + 1, frm); |
| printf("Controller: %d\n", ctr); |
| p_indent(level + 1, frm); |
| major = CAPI_U32(frm); |
| minor = CAPI_U32(frm); |
| printf("CAPI: %d.%d\n", major, minor); |
| major = CAPI_U32(frm); |
| minor = CAPI_U32(frm); |
| p_indent(level + 1, frm); |
| printf("Manufacture: %u.%01x%01x-%02u (%d.%d)\n", |
| (major & 0xf0) >> 4, (major & 0x0f) << 4, |
| (minor & 0xf0) >> 4, minor & 0x0f, |
| major, minor); |
| break; |
| case 5: |
| value = CAPI_U32(frm); |
| p_indent(level + 1, frm); |
| printf("Return value: 0x%04x\n", value); |
| ctr = CAPI_U32(frm); |
| p_indent(level + 1, frm); |
| printf("Controller: %d\n", ctr); |
| CAPI_U8(frm); |
| p_indent(level + 1, frm); |
| printf("Serial number: %.7s\n", (char *) frm->ptr); |
| break; |
| default: |
| raw_dump(level + 1, frm); |
| break; |
| } |
| break; |
| |
| default: |
| raw_dump(level, frm); |
| break; |
| } |
| break; |
| |
| default: |
| p_indent(level, frm); |
| printf("Function: %d\n", func); |
| if (subcmd == 0x81) { |
| p_indent(level, frm); |
| printf("Info: 0x%04x (%s)\n", info, info2str(info)); |
| } |
| raw_dump(level + 1, frm); |
| break; |
| } |
| } |
| |
| static void cmd_facility(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint16_t sel; |
| |
| cmd_common(level, subcmd, frm); |
| |
| sel = CAPI_U16(frm); |
| CAPI_U8(frm); |
| |
| p_indent(level, frm); |
| printf("Selector: 0x%04x (%s)\n", sel, facilitysel2str(sel)); |
| |
| raw_dump(level, frm); |
| } |
| |
| static void cmd_connect_b3(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint16_t reject; |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x81) |
| return; |
| |
| if (subcmd == 0x83) { |
| reject = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Reject: 0x%04x (%s)\n", reject, info2str(reject)); |
| } |
| |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("NCPI:\n"); |
| hex_dump(level, frm, len); |
| } |
| } |
| |
| static void cmd_connect_b3_active(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x82) { |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("NCPI:\n"); |
| hex_dump(level, frm, len); |
| } |
| } |
| } |
| |
| static void cmd_disconnect_b3(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint16_t reason; |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x82) { |
| reason = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Reason: 0x%04x (%s)\n", reason, info2str(reason)); |
| } |
| |
| if (subcmd == 0x80 || subcmd == 0x82) { |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("NCPI:\n"); |
| hex_dump(level, frm, len); |
| } |
| } |
| } |
| |
| static void cmd_data_b3(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint32_t data; |
| uint16_t length, handle, flags, info; |
| |
| cmd_common(level, 0x00, frm); |
| |
| if (subcmd == 0x81 || subcmd == 0x83) { |
| handle = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Data handle: 0x%04x\n", handle); |
| |
| if (subcmd == 0x81) { |
| info = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Info: 0x%04x (%s)\n", info, info2str(info)); |
| } |
| } else { |
| data = CAPI_U32(frm); |
| |
| length = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Data length: 0x%04x (%d bytes)\n", length, length); |
| |
| handle = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Data handle: 0x%04x\n", handle); |
| |
| flags = CAPI_U16(frm); |
| p_indent(level, frm); |
| printf("Flags: 0x%04x\n", flags); |
| |
| if (data == 0) |
| (void) get_u64(frm); |
| |
| raw_dump(level, frm); |
| } |
| } |
| |
| static void cmd_reset_b3(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint8_t len; |
| |
| cmd_common(level, subcmd, frm); |
| |
| if (subcmd == 0x80 || subcmd == 0x82) { |
| len = CAPI_U8(frm); |
| if (len > 0) { |
| p_indent(level, frm); |
| printf("NCPI:\n"); |
| hex_dump(level, frm, len); |
| } |
| } |
| } |
| |
| static void cmd_manufacturer(int level, uint8_t subcmd, struct frame *frm) |
| { |
| uint32_t ctr, class, func; |
| uint16_t len; |
| unsigned char *id; |
| |
| ctr = CAPI_U32(frm); |
| p_indent(level, frm); |
| printf("Controller: %d\n", ctr); |
| |
| id = (unsigned char *) frm->ptr; |
| p_indent(level, frm); |
| if (isprint(id[0]) && isprint(id[1]) && isprint(id[2]) && isprint(id[3])) |
| printf("Manufacturer: %.4s", id); |
| else |
| printf("Manufacturer: 0x%02x 0x%02x 0x%02x 0x%02x", |
| id[0], id[1], id[2], id[3]); |
| frm->ptr += 4; |
| frm->len -= 4; |
| |
| if (!strncmp((char *) id, "AVM!", 4)) { |
| class = CAPI_U32(frm); |
| func = CAPI_U32(frm); |
| len = CAPI_U8(frm); |
| if (len == 0xff) |
| len = CAPI_U16(frm); |
| |
| printf(" [class %d func %d len %d]\n", class, func, len); |
| } else |
| printf("\n"); |
| |
| raw_dump(level, frm); |
| } |
| |
| void capi_dump(int level, struct frame *frm) |
| { |
| uint16_t len, appl, msgnum; |
| uint8_t cmd, subcmd; |
| |
| len = CAPI_U16(frm) - 8; |
| appl = CAPI_U16(frm); |
| cmd = CAPI_U8(frm); |
| subcmd = CAPI_U8(frm); |
| msgnum = CAPI_U16(frm); |
| |
| p_indent(level, frm); |
| |
| printf("CAPI_%s_%s: appl %d msgnum %d len %d\n", |
| cmd2str(cmd), subcmd2str(subcmd), appl, msgnum, len); |
| |
| switch (cmd) { |
| case 0x01: |
| cmd_alert(level + 1, subcmd, frm); |
| break; |
| case 0x02: |
| cmd_connect(level + 1, subcmd, frm); |
| break; |
| case 0x03: |
| cmd_connect_active(level + 1, subcmd, frm); |
| break; |
| case 0x04: |
| cmd_disconnect(level + 1, subcmd, frm); |
| break; |
| case 0x05: |
| cmd_listen(level + 1, subcmd, frm); |
| break; |
| case 0x08: |
| cmd_info(level + 1, subcmd, frm); |
| break; |
| case 0x20: |
| cmd_interoperability(level + 1, subcmd, frm); |
| break; |
| case 0x80: |
| cmd_facility(level + 1, subcmd, frm); |
| break; |
| case 0x82: |
| cmd_connect_b3(level + 1, subcmd, frm); |
| break; |
| case 0x83: |
| case 0x88: |
| cmd_connect_b3_active(level + 1, subcmd, frm); |
| break; |
| case 0x84: |
| cmd_disconnect_b3(level + 1, subcmd, frm); |
| break; |
| case 0x86: |
| cmd_data_b3(level + 1, subcmd, frm); |
| break; |
| case 0x87: |
| cmd_reset_b3(level + 1, subcmd, frm); |
| break; |
| case 0xff: |
| cmd_manufacturer(level + 1, subcmd, frm); |
| break; |
| default: |
| raw_dump(level, frm); |
| frm->ptr += len; |
| frm->len -= len; |
| break; |
| } |
| } |