/*
|
* BlueALSA - hcitop.c
|
* Copyright (c) 2016-2018 Arkadiusz Bokowy
|
*
|
* This file is a part of bluez-alsa.
|
*
|
* This project is licensed under the terms of the MIT license.
|
*
|
*/
|
|
#if HAVE_CONFIG_H
|
# include "config.h"
|
#endif
|
|
#include <getopt.h>
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <unistd.h>
|
|
#include <ncurses.h>
|
#include <bsd/stdlib.h>
|
|
#include <bluetooth/bluetooth.h>
|
#include <bluetooth/hci.h>
|
#include <bluetooth/hci_lib.h>
|
|
static const struct {
|
unsigned int bit;
|
char flag;
|
} hci_flags_map[] = {
|
{ HCI_UP, 'U' },
|
{ HCI_INIT, 'I' },
|
{ HCI_RUNNING, 'R' },
|
{ HCI_PSCAN, 'P' },
|
{ HCI_ISCAN, 'I' },
|
{ HCI_AUTH, 'A' },
|
{ HCI_ENCRYPT, 'E' },
|
{ HCI_INQUIRY, 'Q' },
|
{ HCI_RAW, 'X' },
|
};
|
|
static int get_devinfo(struct hci_dev_info di[HCI_MAX_DEV]) {
|
|
int i, num;
|
|
for (i = num = 0; i < HCI_MAX_DEV; i++)
|
if (hci_devinfo(i, &di[num]) == 0)
|
num++;
|
|
return num;
|
}
|
|
static unsigned int get_average_rate(unsigned int *array, size_t size) {
|
|
/* at least two points are required */
|
if (size < 2)
|
return 0;
|
|
unsigned int x = 0;
|
unsigned int y = 0;
|
size_t i;
|
|
size--;
|
i = size;
|
|
while (i--) {
|
int b = (array[i] - array[i + 1]) % size;
|
x += (array[i] - array[i + 1]) / size;
|
if (y >= size - b) {
|
y -= size - b;
|
x++;
|
}
|
else {
|
y += b;
|
}
|
}
|
|
return x + y / size;
|
}
|
|
static void sprint_hci_flags(char *str, unsigned int flags) {
|
|
size_t i;
|
|
for (i = 0; i < sizeof(hci_flags_map) / sizeof(*hci_flags_map); i++)
|
str[i] = hci_test_bit(hci_flags_map[i].bit, &flags) ? hci_flags_map[i].flag : ' ';
|
|
str[i] = '\0';
|
}
|
|
int main(int argc, char *argv[]) {
|
|
int opt;
|
const char *opts = "hVd:";
|
const struct option longopts[] = {
|
{ "help", no_argument, NULL, 'h' },
|
{ "version", no_argument, NULL, 'V' },
|
{ "delay", required_argument, NULL, 'd' },
|
{ 0, 0, 0, 0 },
|
};
|
|
int delay_sec = 1;
|
int delay_msec = 0;
|
|
while ((opt = getopt_long(argc, argv, opts, longopts, NULL)) != -1)
|
switch (opt) {
|
case 'h' /* --help */ :
|
printf("usage: %s [ -d sec ]\n"
|
" -h, --help\t\tprint this help and exit\n"
|
" -V, --version\t\tprint version and exit\n"
|
" -d, --delay=SEC\tdelay time interval\n",
|
argv[0]);
|
return EXIT_SUCCESS;
|
|
case 'V' /* --version */ :
|
printf("%s\n", PACKAGE_VERSION);
|
return EXIT_SUCCESS;
|
|
case 'd' /* --delay=SEC */ :
|
delay_sec = atoi(optarg);
|
delay_msec = (int)((atof(optarg) - delay_sec) * 10) * 100;
|
if (delay_sec < 0 || delay_msec < 0 || (delay_sec == 0 && delay_msec == 0)) {
|
fprintf(stderr, "%s: -d requires positive argument (max precision: 0.1)\n", argv[0]);
|
return EXIT_FAILURE;
|
}
|
break;
|
|
default:
|
fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
|
return EXIT_FAILURE;
|
}
|
|
struct hci_dev_info devices[HCI_MAX_DEV];
|
unsigned int byte_rx[HCI_MAX_DEV][3];
|
unsigned int byte_tx[HCI_MAX_DEV][3];
|
size_t ii;
|
|
memset(byte_rx, 0, sizeof(byte_rx));
|
memset(byte_tx, 0, sizeof(byte_tx));
|
|
initscr();
|
cbreak();
|
noecho();
|
curs_set(0);
|
|
for (ii = 1;; ii++) {
|
|
const char *template_top = "%5s %9s %8s %8s %8s %8s";
|
const char *template_row = "%5s %9s %8s %8s %8s %8s";
|
int i, count;
|
|
attron(A_REVERSE);
|
mvprintw(0, 0, template_top, "HCI", "FLAGS", "RX", "TX", "RX/s", "TX/s");
|
attroff(A_REVERSE);
|
|
count = get_devinfo(devices);
|
for (i = 0; i < HCI_MAX_DEV; i++) {
|
|
/* shift historic data to the right by one sample */
|
memmove(&byte_rx[i][1], &byte_rx[i][0], sizeof(*byte_rx) - sizeof(**byte_rx));
|
memmove(&byte_tx[i][1], &byte_tx[i][0], sizeof(*byte_tx) - sizeof(**byte_tx));
|
|
if (i >= count)
|
continue;
|
|
char flags[sizeof(hci_flags_map) / sizeof(*hci_flags_map) + 1];
|
|
sprint_hci_flags(flags, devices[i].flags);
|
|
byte_rx[i][0] = devices[i].stat.byte_rx;
|
byte_tx[i][0] = devices[i].stat.byte_tx;
|
|
const size_t ii_max = sizeof(*byte_rx) / sizeof(**byte_rx);
|
const size_t samples = ii < ii_max ? ii : ii_max;
|
unsigned int rate_rx = get_average_rate(byte_rx[i], samples);
|
unsigned int rate_tx = get_average_rate(byte_tx[i], samples);
|
|
rate_rx = rate_rx * 10 / (delay_sec * 10 + delay_msec / 100);
|
rate_tx = rate_tx * 10 / (delay_sec * 10 + delay_msec / 100);
|
|
char rx[7], rx_rate[9];
|
char tx[7], tx_rate[9];
|
|
humanize_number(rx, sizeof(rx), byte_rx[i][0], "B", HN_AUTOSCALE, 0);
|
humanize_number(tx, sizeof(tx), byte_tx[i][0], "B", HN_AUTOSCALE, 0);
|
humanize_number(rx_rate, sizeof(rx_rate), rate_rx, "B", HN_AUTOSCALE, 0);
|
humanize_number(tx_rate, sizeof(tx_rate), rate_tx, "B", HN_AUTOSCALE, 0);
|
|
mvprintw(i + 1, 0, template_row, devices[i].name, flags, rx, tx, rx_rate, tx_rate);
|
}
|
|
timeout(delay_sec * 1000 + delay_msec);
|
if (getch() == 'q')
|
break;
|
|
}
|
|
endwin();
|
return EXIT_SUCCESS;
|
}
|