Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/next/external/cache/sources/hcitools/monitor/ll.c
Views: 3959
/*1*2* BlueZ - Bluetooth protocol stack for Linux3*4* Copyright (C) 2011-2012 Intel Corporation5* Copyright (C) 2004-2010 Marcel Holtmann <[email protected]>6*7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (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 of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA21*22*/2324#ifdef HAVE_CONFIG_H25#include <config.h>26#endif2728#include <inttypes.h>2930#include <bluetooth/bluetooth.h>3132#include "display.h"33#include "packet.h"34#include "crc.h"35#include "bt.h"36#include "ll.h"3738#define COLOR_OPCODE COLOR_MAGENTA39#define COLOR_OPCODE_UNKNOWN COLOR_WHITE_BG4041#define MAX_CHANNEL 164243struct channel_data {44uint32_t access_addr;45uint32_t crc_init;46};4748static struct channel_data channel_list[MAX_CHANNEL];4950static void set_crc_init(uint32_t access_addr, uint32_t crc_init)51{52int i;5354for (i = 0; i < MAX_CHANNEL; i++) {55if (channel_list[i].access_addr == 0x00000000 ||56channel_list[i].access_addr == access_addr) {57channel_list[i].access_addr = access_addr;58channel_list[i].crc_init = crc_init;59break;60}61}62}6364static uint32_t get_crc_init(uint32_t access_addr)65{66int i;6768for (i = 0; i < MAX_CHANNEL; i++) {69if (channel_list[i].access_addr == access_addr)70return channel_list[i].crc_init;71}7273return 0x00000000;74}7576static void advertising_packet(const void *data, uint8_t size)77{78const uint8_t *ptr = data;79uint8_t pdu_type, length, win_size, hop, sca;80bool tx_add, rx_add;81uint32_t access_addr, crc_init;82uint16_t win_offset, interval, latency, timeout;83const char *str;8485if (size < 2) {86print_text(COLOR_ERROR, "packet too short");87packet_hexdump(data, size);88return;89}9091pdu_type = ptr[0] & 0x0f;92tx_add = !!(ptr[0] & 0x40);93rx_add = !!(ptr[0] & 0x80);94length = ptr[1] & 0x3f;9596switch (pdu_type) {97case 0x00:98str = "ADV_IND";99break;100case 0x01:101str = "ADV_DIRECT_IND";102break;103case 0x02:104str = "ADV_NONCONN_IND";105break;106case 0x03:107str = "SCAN_REQ";108break;109case 0x04:110str = "SCAN_RSP";111break;112case 0x05:113str = "CONNECT_REQ";114break;115case 0x06:116str = "ADV_SCAN_IND";117break;118default:119str = "Reserved";120break;121}122123print_field("Type: %s (0x%2.2x)", str, pdu_type);124print_field("TxAdd: %u", tx_add);125print_field("RxAdd: %u", rx_add);126print_field("Length: %u", length);127128if (length != size - 2) {129print_text(COLOR_ERROR, "packet size mismatch");130packet_hexdump(data + 2, size - 2);131return;132}133134switch (pdu_type) {135case 0x00: /* ADV_IND */136case 0x02: /* AVD_NONCONN_IND */137case 0x06: /* ADV_SCAN_IND */138case 0x04: /* SCAN_RSP */139if (length < 6) {140print_text(COLOR_ERROR, "payload too short");141packet_hexdump(data + 2, length);142return;143}144145packet_print_addr("Advertiser address", data + 2, tx_add);146packet_print_ad(data + 8, length - 6);147break;148149case 0x01: /* ADV_DIRECT_IND */150if (length < 12) {151print_text(COLOR_ERROR, "payload too short");152packet_hexdump(data + 2, length);153return;154}155156packet_print_addr("Advertiser address", data + 2, tx_add);157packet_print_addr("Inititator address", data + 8, rx_add);158break;159160case 0x03: /* SCAN_REQ */161if (length < 12) {162print_text(COLOR_ERROR, "payload too short");163packet_hexdump(data + 2, length);164return;165}166167packet_print_addr("Scanner address", data + 2, tx_add);168packet_print_addr("Advertiser address", data + 8, rx_add);169break;170171case 0x05: /* CONNECT_REQ */172if (length < 34) {173print_text(COLOR_ERROR, "payload too short");174packet_hexdump(data + 2, length);175return;176}177178packet_print_addr("Inititator address", data + 2, tx_add);179packet_print_addr("Advertiser address", data + 8, rx_add);180181access_addr = ptr[14] | ptr[15] << 8 |182ptr[16] << 16 | ptr[17] << 24;183crc_init = ptr[18] | ptr[19] << 8 | ptr[20] << 16;184185print_field("Access address: 0x%8.8x", access_addr);186print_field("CRC init: 0x%6.6x", crc_init);187188set_crc_init(access_addr, crc24_bit_reverse(crc_init));189190win_size = ptr[21];191win_offset = ptr[22] | ptr[23] << 8;192interval = ptr[24] | ptr[25] << 8;193latency = ptr[26] | ptr[27] << 8;194timeout = ptr[28] | ptr[29] << 8;195196print_field("Transmit window size: %u", win_size);197print_field("Transmit window offset: %u", win_offset);198print_field("Connection interval: %u", interval);199print_field("Connection slave latency: %u", latency);200print_field("Connection supervision timeout: %u", timeout);201202packet_print_channel_map_ll(ptr + 30);203204hop = ptr[35] & 0x1f;205sca = (ptr[35] & 0xe0) >> 5;206207switch (sca) {208case 0:209str = "251 ppm to 500 ppm";210break;211case 1:212str = "151 ppm to 250 ppm";213break;214case 2:215str = "101 ppm to 150ppm";216break;217case 3:218str = "76 ppm to 100 ppm";219break;220case 4:221str = "51 ppm to 75 ppm";222break;223case 5:224str = "31 ppm to 50 ppm";225break;226case 6:227str = "21 ppm to 30 ppm";228break;229case 7:230str = "0 ppm to 20 ppm";231break;232default:233str = "Invalid";234break;235}236237print_field("Hop increment: %u", hop);238print_field("Sleep clock accuracy: %s (%u)", str, sca);239break;240241default:242packet_hexdump(data + 2, length);243break;244}245}246247static void data_packet(const void *data, uint8_t size)248{249const uint8_t *ptr = data;250uint8_t llid, length;251bool nesn, sn, md;252const char *str;253254if (size < 2) {255print_text(COLOR_ERROR, "packet too short");256packet_hexdump(data, size);257return;258}259260llid = ptr[0] & 0x03;261nesn = !!(ptr[0] & 0x04);262sn = !!(ptr[0] & 0x08);263md = !!(ptr[0] & 0x10);264length = ptr[1] & 0x1f;265266switch (llid) {267case 0x01:268if (length > 0)269str = "Continuation fragement of L2CAP message";270else271str = "Empty message";272break;273case 0x02:274str = "Start of L2CAP message";275break;276case 0x03:277str = "Control";278break;279default:280str = "Reserved";281break;282}283284print_field("LLID: %s (0x%2.2x)", str, llid);285print_field("Next expected sequence number: %u", nesn);286print_field("Sequence number: %u", sn);287print_field("More data: %u", md);288print_field("Length: %u", length);289290switch (llid) {291case 0x03:292llcp_packet(data + 2, size - 2);293break;294295default:296packet_hexdump(data + 2, size - 2);297break;298}299}300301void ll_packet(uint16_t frequency, const void *data, uint8_t size)302{303const struct bt_ll_hdr *hdr = data;304uint8_t channel = (frequency - 2402) / 2;305uint32_t access_addr;306char access_str[12];307const char *channel_label, *channel_color;308const uint8_t *pdu_data;309uint8_t pdu_len;310uint32_t pdu_crc, crc, crc_init;311312if (size < sizeof(*hdr)) {313print_text(COLOR_ERROR, "packet missing header");314packet_hexdump(data, size);315return;316}317318if (size < sizeof(*hdr) + 3) {319print_text(COLOR_ERROR, "packet missing checksum");320packet_hexdump(data, size);321return;322}323324if (hdr->preamble != 0xaa && hdr->preamble != 0x55) {325print_text(COLOR_ERROR, "invalid preamble");326packet_hexdump(data, size);327return;328}329330access_addr = btohl(hdr->access_addr);331332pdu_data = data + sizeof(*hdr);333pdu_len = size - sizeof(*hdr) - 3;334335pdu_crc = pdu_data[pdu_len + 0] | (pdu_data[pdu_len + 1] << 8) |336(pdu_data[pdu_len + 2] << 16);337338if (access_addr == 0x8e89bed6) {339channel_label = "Advertising channel: ";340channel_color = COLOR_MAGENTA;341} else {342channel_label = "Data channel: ";343channel_color = COLOR_CYAN;344}345346sprintf(access_str, "0x%8.8x", access_addr);347348print_indent(6, channel_color, channel_label, access_str, COLOR_OFF,349" (channel %d) len %d crc 0x%6.6x", channel, pdu_len, pdu_crc);350351if (access_addr == 0x8e89bed6)352crc_init = 0xaaaaaa;353else354crc_init = get_crc_init(access_addr);355356if (crc_init) {357crc = crc24_calculate(crc_init, pdu_data, pdu_len);358359if (crc != pdu_crc) {360print_text(COLOR_ERROR, "invalid checksum");361packet_hexdump(pdu_data, pdu_len);362return;363}364} else365print_text(COLOR_ERROR, "unknown access address");366367if (access_addr == 0x8e89bed6)368advertising_packet(pdu_data, pdu_len);369else370data_packet(pdu_data, pdu_len);371}372373static void null_pdu(const void *data, uint8_t size)374{375}376377static void conn_update_req(const void *data, uint8_t size)378{379const struct bt_ll_conn_update_req *pdu = data;380381print_field("Transmit window size: %u", pdu->win_size);382print_field("Transmit window offset: %u", btohs(pdu->win_offset));383print_field("Connection interval: %u", btohs(pdu->interval));384print_field("Connection slave latency: %u", btohs(pdu->latency));385print_field("Connection supervision timeout: %u", btohs(pdu->timeout));;386print_field("Connection instant: %u", btohs(pdu->instant));387}388389static void channel_map_req(const void *data, uint8_t size)390{391const struct bt_ll_channel_map_req *pdu = data;392393packet_print_channel_map_ll(pdu->map);394print_field("Connection instant: %u", btohs(pdu->instant));395}396397static void terminate_ind(const void *data, uint8_t size)398{399const struct bt_ll_terminate_ind *pdu = data;400401print_field("Error code: 0x%2.2x", pdu->error);402}403404static void enc_req(const void *data, uint8_t size)405{406const struct bt_ll_enc_req *pdu = data;407408print_field("Rand: 0x%16.16" PRIx64, btohll(pdu->rand));409print_field("EDIV: 0x%4.4x", btohs(pdu->ediv));410print_field("SKD (master): 0x%16.16" PRIx64, btohll(pdu->skd));411print_field("IV (master): 0x%8.8x", btohl(pdu->iv));412}413414static void enc_rsp(const void *data, uint8_t size)415{416const struct bt_ll_enc_rsp *pdu = data;417418print_field("SKD (slave): 0x%16.16" PRIx64, btohll(pdu->skd));419print_field("IV (slave): 0x%8.8x", btohl(pdu->iv));420}421422static const char *opcode_to_string(uint8_t opcode);423424static void unknown_rsp(const void *data, uint8_t size)425{426const struct bt_ll_unknown_rsp *pdu = data;427428print_field("Unknown type: %s (0x%2.2x)",429opcode_to_string(pdu->type), pdu->type);430}431432static void feature_req(const void *data, uint8_t size)433{434const struct bt_ll_feature_req *pdu = data;435436packet_print_features_ll(pdu->features);437}438439static void feature_rsp(const void *data, uint8_t size)440{441const struct bt_ll_feature_rsp *pdu = data;442443packet_print_features_ll(pdu->features);444}445446static void version_ind(const void *data, uint8_t size)447{448const struct bt_ll_version_ind *pdu = data;449450packet_print_version("Version", pdu->version,451"Subversion", btohs(pdu->subversion));452packet_print_company("Company", btohs(pdu->company));453}454455static void reject_ind(const void *data, uint8_t size)456{457const struct bt_ll_reject_ind *pdu = data;458459print_field("Error code: 0x%2.2x", pdu->error);460}461462struct llcp_data {463uint8_t opcode;464const char *str;465void (*func) (const void *data, uint8_t size);466uint8_t size;467bool fixed;468};469470static const struct llcp_data llcp_table[] = {471{ 0x00, "LL_CONNECTION_UPDATE_REQ", conn_update_req, 11, true },472{ 0x01, "LL_CHANNEL_MAP_REQ", channel_map_req, 7, true },473{ 0x02, "LL_TERMINATE_IND", terminate_ind, 1, true },474{ 0x03, "LL_ENC_REQ", enc_req, 22, true },475{ 0x04, "LL_ENC_RSP", enc_rsp, 12, true },476{ 0x05, "LL_START_ENC_REQ", null_pdu, 0, true },477{ 0x06, "LL_START_ENC_RSP", null_pdu, 0, true },478{ 0x07, "LL_UNKNOWN_RSP", unknown_rsp, 1, true },479{ 0x08, "LL_FEATURE_REQ", feature_req, 8, true },480{ 0x09, "LL_FEATURE_RSP", feature_rsp, 8, true },481{ 0x0a, "LL_PAUSE_ENC_REQ", null_pdu, 0, true },482{ 0x0b, "LL_PAUSE_ENC_RSP", null_pdu, 0, true },483{ 0x0c, "LL_VERSION_IND", version_ind, 5, true },484{ 0x0d, "LL_REJECT_IND", reject_ind, 1, true },485{ }486};487488static const char *opcode_to_string(uint8_t opcode)489{490int i;491492for (i = 0; llcp_table[i].str; i++) {493if (llcp_table[i].opcode == opcode)494return llcp_table[i].str;495}496497return "Unknown";498}499500void llcp_packet(const void *data, uint8_t size)501{502uint8_t opcode = ((const uint8_t *) data)[0];503const struct llcp_data *llcp_data = NULL;504const char *opcode_color, *opcode_str;505int i;506507for (i = 0; llcp_table[i].str; i++) {508if (llcp_table[i].opcode == opcode) {509llcp_data = &llcp_table[i];510break;511}512}513514if (llcp_data) {515if (llcp_data->func)516opcode_color = COLOR_OPCODE;517else518opcode_color = COLOR_OPCODE_UNKNOWN;519opcode_str = llcp_data->str;520} else {521opcode_color = COLOR_OPCODE_UNKNOWN;522opcode_str = "Unknown";523}524525print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,526" (0x%2.2x)", opcode);527528if (!llcp_data || !llcp_data->func) {529packet_hexdump(data + 1, size - 1);530return;531}532533if (llcp_data->fixed) {534if (size - 1 != llcp_data->size) {535print_text(COLOR_ERROR, "invalid packet size");536packet_hexdump(data + 1, size - 1);537return;538}539} else {540if (size - 1 < llcp_data->size) {541print_text(COLOR_ERROR, "too short packet");542packet_hexdump(data + 1, size - 1);543return;544}545}546547llcp_data->func(data + 1, size - 1);548}549550551