blob: 788f4bdb4a496e4e561cd65b4541d7f7856ba3f2 [file] [log] [blame]
/*
*
* 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 <sys/types.h>
#include <netinet/in.h>
#include <bluetooth/bluetooth.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;
uint64_t data64;
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)
data64 = 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;
}
}