mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-30 07:34:27 +08:00
1255 lines
28 KiB
C
1255 lines
28 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2004-2010 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 <stdlib.h>
|
|
#include <getopt.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/hci_lib.h>
|
|
|
|
#include "csr.h"
|
|
|
|
#define CSR_TRANSPORT_UNKNOWN 0
|
|
#define CSR_TRANSPORT_HCI 1
|
|
#define CSR_TRANSPORT_USB 2
|
|
#define CSR_TRANSPORT_BCSP 3
|
|
#define CSR_TRANSPORT_H4 4
|
|
#define CSR_TRANSPORT_3WIRE 5
|
|
|
|
#define CSR_STORES_PSI (0x0001)
|
|
#define CSR_STORES_PSF (0x0002)
|
|
#define CSR_STORES_PSROM (0x0004)
|
|
#define CSR_STORES_PSRAM (0x0008)
|
|
#define CSR_STORES_DEFAULT (CSR_STORES_PSI | CSR_STORES_PSF)
|
|
|
|
#define CSR_TYPE_NULL 0
|
|
#define CSR_TYPE_COMPLEX 1
|
|
#define CSR_TYPE_UINT8 2
|
|
#define CSR_TYPE_UINT16 3
|
|
#define CSR_TYPE_UINT32 4
|
|
|
|
#define CSR_TYPE_ARRAY CSR_TYPE_COMPLEX
|
|
#define CSR_TYPE_BDADDR CSR_TYPE_COMPLEX
|
|
|
|
static inline int transport_open(int transport, char *device, speed_t bcsp_rate)
|
|
{
|
|
switch (transport) {
|
|
case CSR_TRANSPORT_HCI:
|
|
return csr_open_hci(device);
|
|
#ifdef HAVE_LIBUSB
|
|
case CSR_TRANSPORT_USB:
|
|
return csr_open_usb(device);
|
|
#endif
|
|
case CSR_TRANSPORT_BCSP:
|
|
return csr_open_bcsp(device, bcsp_rate);
|
|
case CSR_TRANSPORT_H4:
|
|
return csr_open_h4(device);
|
|
case CSR_TRANSPORT_3WIRE:
|
|
return csr_open_3wire(device);
|
|
default:
|
|
fprintf(stderr, "Unsupported transport\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static inline int transport_read(int transport, uint16_t varid, uint8_t *value, uint16_t length)
|
|
{
|
|
switch (transport) {
|
|
case CSR_TRANSPORT_HCI:
|
|
return csr_read_hci(varid, value, length);
|
|
#ifdef HAVE_LIBUSB
|
|
case CSR_TRANSPORT_USB:
|
|
return csr_read_usb(varid, value, length);
|
|
#endif
|
|
case CSR_TRANSPORT_BCSP:
|
|
return csr_read_bcsp(varid, value, length);
|
|
case CSR_TRANSPORT_H4:
|
|
return csr_read_h4(varid, value, length);
|
|
case CSR_TRANSPORT_3WIRE:
|
|
return csr_read_3wire(varid, value, length);
|
|
default:
|
|
errno = EOPNOTSUPP;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static inline int transport_write(int transport, uint16_t varid, uint8_t *value, uint16_t length)
|
|
{
|
|
switch (transport) {
|
|
case CSR_TRANSPORT_HCI:
|
|
return csr_write_hci(varid, value, length);
|
|
#ifdef HAVE_LIBUSB
|
|
case CSR_TRANSPORT_USB:
|
|
return csr_write_usb(varid, value, length);
|
|
#endif
|
|
case CSR_TRANSPORT_BCSP:
|
|
return csr_write_bcsp(varid, value, length);
|
|
case CSR_TRANSPORT_H4:
|
|
return csr_write_h4(varid, value, length);
|
|
case CSR_TRANSPORT_3WIRE:
|
|
return csr_write_3wire(varid, value, length);
|
|
default:
|
|
errno = EOPNOTSUPP;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static inline void transport_close(int transport)
|
|
{
|
|
switch (transport) {
|
|
case CSR_TRANSPORT_HCI:
|
|
csr_close_hci();
|
|
break;
|
|
#ifdef HAVE_LIBUSB
|
|
case CSR_TRANSPORT_USB:
|
|
csr_close_usb();
|
|
break;
|
|
#endif
|
|
case CSR_TRANSPORT_BCSP:
|
|
csr_close_bcsp();
|
|
break;
|
|
case CSR_TRANSPORT_H4:
|
|
csr_close_h4();
|
|
break;
|
|
case CSR_TRANSPORT_3WIRE:
|
|
csr_close_3wire();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static struct {
|
|
uint16_t pskey;
|
|
int type;
|
|
int size;
|
|
char *str;
|
|
} storage[] = {
|
|
{ CSR_PSKEY_BDADDR, CSR_TYPE_BDADDR, 8, "bdaddr" },
|
|
{ CSR_PSKEY_COUNTRYCODE, CSR_TYPE_UINT16, 0, "country" },
|
|
{ CSR_PSKEY_CLASSOFDEVICE, CSR_TYPE_UINT32, 0, "devclass" },
|
|
{ CSR_PSKEY_ENC_KEY_LMIN, CSR_TYPE_UINT16, 0, "keymin" },
|
|
{ CSR_PSKEY_ENC_KEY_LMAX, CSR_TYPE_UINT16, 0, "keymax" },
|
|
{ CSR_PSKEY_LOCAL_SUPPORTED_FEATURES, CSR_TYPE_ARRAY, 8, "features" },
|
|
{ CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS, CSR_TYPE_ARRAY, 18, "commands" },
|
|
{ CSR_PSKEY_HCI_LMP_LOCAL_VERSION, CSR_TYPE_UINT16, 0, "version" },
|
|
{ CSR_PSKEY_LMP_REMOTE_VERSION, CSR_TYPE_UINT8, 0, "remver" },
|
|
{ CSR_PSKEY_HOSTIO_USE_HCI_EXTN, CSR_TYPE_UINT16, 0, "hciextn" },
|
|
{ CSR_PSKEY_HOSTIO_MAP_SCO_PCM, CSR_TYPE_UINT16, 0, "mapsco" },
|
|
{ CSR_PSKEY_UART_BAUDRATE, CSR_TYPE_UINT16, 0, "baudrate" },
|
|
{ CSR_PSKEY_HOST_INTERFACE, CSR_TYPE_UINT16, 0, "hostintf" },
|
|
{ CSR_PSKEY_ANA_FREQ, CSR_TYPE_UINT16, 0, "anafreq" },
|
|
{ CSR_PSKEY_ANA_FTRIM, CSR_TYPE_UINT16, 0, "anaftrim" },
|
|
{ CSR_PSKEY_USB_VENDOR_ID, CSR_TYPE_UINT16, 0, "usbvid" },
|
|
{ CSR_PSKEY_USB_PRODUCT_ID, CSR_TYPE_UINT16, 0, "usbpid" },
|
|
{ CSR_PSKEY_USB_DFU_PRODUCT_ID, CSR_TYPE_UINT16, 0, "dfupid" },
|
|
{ CSR_PSKEY_INITIAL_BOOTMODE, CSR_TYPE_UINT16, 0, "bootmode" },
|
|
{ 0x0000 },
|
|
};
|
|
|
|
static char *storestostr(uint16_t stores)
|
|
{
|
|
switch (stores) {
|
|
case 0x0000:
|
|
return "Default";
|
|
case 0x0001:
|
|
return "psi";
|
|
case 0x0002:
|
|
return "psf";
|
|
case 0x0004:
|
|
return "psrom";
|
|
case 0x0008:
|
|
return "psram";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
static char *memorytostr(uint16_t type)
|
|
{
|
|
switch (type) {
|
|
case 0x0000:
|
|
return "Flash memory";
|
|
case 0x0001:
|
|
return "EEPROM";
|
|
case 0x0002:
|
|
return "RAM (transient)";
|
|
case 0x0003:
|
|
return "ROM (or \"read-only\" flash memory)";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
#define OPT_RANGE(min, max) \
|
|
if (argc < (min)) { errno = EINVAL; return -1; } \
|
|
if (argc > (max)) { errno = E2BIG; return -1; }
|
|
|
|
static struct option help_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static int opt_help(int argc, char *argv[], int *help)
|
|
{
|
|
int opt;
|
|
|
|
while ((opt=getopt_long(argc, argv, "+h", help_options, NULL)) != EOF) {
|
|
switch (opt) {
|
|
case 'h':
|
|
if (help)
|
|
*help = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return optind;
|
|
}
|
|
|
|
#define OPT_HELP(range, help) \
|
|
opt_help(argc, argv, (help)); \
|
|
argc -= optind; argv += optind; optind = 0; \
|
|
OPT_RANGE((range), (range))
|
|
|
|
static int cmd_builddef(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t def = 0x0000, nextdef = 0x0000;
|
|
int err = 0;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
printf("Build definitions:\n");
|
|
|
|
while (1) {
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = def & 0xff;
|
|
array[1] = def >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_GET_NEXT_BUILDDEF, array, 8);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
break;
|
|
}
|
|
|
|
nextdef = array[2] | (array[3] << 8);
|
|
|
|
if (nextdef == 0x0000)
|
|
break;
|
|
|
|
def = nextdef;
|
|
|
|
printf("0x%04x - %s\n", def, csr_builddeftostr(def));
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int cmd_keylen(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t handle, keylen;
|
|
int err;
|
|
|
|
OPT_HELP(1, NULL);
|
|
|
|
handle = atoi(argv[0]);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = handle & 0xff;
|
|
array[1] = handle >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_CRYPT_KEY_LENGTH, array, 8);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
handle = array[0] | (array[1] << 8);
|
|
keylen = array[2] | (array[3] << 8);
|
|
|
|
printf("Crypt key length: %d bit\n", keylen * 8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_clock(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint32_t clock;
|
|
int err;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
err = transport_read(transport, CSR_VARID_BT_CLOCK, array, 8);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
clock = array[2] | (array[3] << 8) | (array[0] << 16) | (array[1] << 24);
|
|
|
|
printf("Bluetooth clock: 0x%04x (%d)\n", clock, clock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_rand(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t rand;
|
|
int err;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
err = transport_read(transport, CSR_VARID_RAND, array, 8);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
rand = array[0] | (array[1] << 8);
|
|
|
|
printf("Random number: 0x%02x (%d)\n", rand, rand);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_chiprev(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t rev;
|
|
char *str;
|
|
int err;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
err = transport_read(transport, CSR_VARID_CHIPREV, array, 8);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
rev = array[0] | (array[1] << 8);
|
|
|
|
switch (rev) {
|
|
case 0x64:
|
|
str = "BC1 ES";
|
|
break;
|
|
case 0x65:
|
|
str = "BC1";
|
|
break;
|
|
case 0x89:
|
|
str = "BC2-External A";
|
|
break;
|
|
case 0x8a:
|
|
str = "BC2-External B";
|
|
break;
|
|
case 0x28:
|
|
str = "BC2-ROM";
|
|
break;
|
|
case 0x43:
|
|
str = "BC3-Multimedia";
|
|
break;
|
|
case 0x15:
|
|
str = "BC3-ROM";
|
|
break;
|
|
case 0xe2:
|
|
str = "BC3-Flash";
|
|
break;
|
|
case 0x26:
|
|
str = "BC4-External";
|
|
break;
|
|
case 0x30:
|
|
str = "BC4-ROM";
|
|
break;
|
|
default:
|
|
str = "NA";
|
|
break;
|
|
}
|
|
|
|
printf("Chip revision: 0x%04x (%s)\n", rev, str);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_buildname(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[130];
|
|
char name[64];
|
|
unsigned int i;
|
|
int err;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
err = transport_read(transport, CSR_VARID_READ_BUILD_NAME, array, 128);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < sizeof(name); i++)
|
|
name[i] = array[(i * 2) + 4];
|
|
|
|
printf("Build name: %s\n", name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_panicarg(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t error;
|
|
int err;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
err = transport_read(transport, CSR_VARID_PANIC_ARG, array, 8);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
error = array[0] | (array[1] << 8);
|
|
|
|
printf("Panic code: 0x%02x (%s)\n", error,
|
|
error < 0x100 ? "valid" : "invalid");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_faultarg(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t error;
|
|
int err;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
err = transport_read(transport, CSR_VARID_FAULT_ARG, array, 8);
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
error = array[0] | (array[1] << 8);
|
|
|
|
printf("Fault code: 0x%02x (%s)\n", error,
|
|
error < 0x100 ? "valid" : "invalid");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_coldreset(int transport, int argc, char *argv[])
|
|
{
|
|
return transport_write(transport, CSR_VARID_COLD_RESET, NULL, 0);
|
|
}
|
|
|
|
static int cmd_warmreset(int transport, int argc, char *argv[])
|
|
{
|
|
return transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
|
|
}
|
|
|
|
static int cmd_disabletx(int transport, int argc, char *argv[])
|
|
{
|
|
return transport_write(transport, CSR_VARID_DISABLE_TX, NULL, 0);
|
|
}
|
|
|
|
static int cmd_enabletx(int transport, int argc, char *argv[])
|
|
{
|
|
return transport_write(transport, CSR_VARID_ENABLE_TX, NULL, 0);
|
|
}
|
|
|
|
static int cmd_singlechan(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t channel;
|
|
|
|
OPT_HELP(1, NULL);
|
|
|
|
channel = atoi(argv[0]);
|
|
|
|
if (channel > 2401 && channel < 2481)
|
|
channel -= 2402;
|
|
|
|
if (channel > 78) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = channel & 0xff;
|
|
array[1] = channel >> 8;
|
|
|
|
return transport_write(transport, CSR_VARID_SINGLE_CHAN, array, 8);
|
|
}
|
|
|
|
static int cmd_hoppingon(int transport, int argc, char *argv[])
|
|
{
|
|
return transport_write(transport, CSR_VARID_HOPPING_ON, NULL, 0);
|
|
}
|
|
|
|
static int cmd_rttxdata1(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t freq, level;
|
|
|
|
OPT_HELP(2, NULL);
|
|
|
|
freq = atoi(argv[0]);
|
|
|
|
if (!strncasecmp(argv[1], "0x", 2))
|
|
level = strtol(argv[1], NULL, 16);
|
|
else
|
|
level = atoi(argv[1]);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = 0x04;
|
|
array[1] = 0x00;
|
|
array[2] = freq & 0xff;
|
|
array[3] = freq >> 8;
|
|
array[4] = level & 0xff;
|
|
array[5] = level >> 8;
|
|
|
|
return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
|
|
}
|
|
|
|
static int cmd_radiotest(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t freq, level, test;
|
|
|
|
OPT_HELP(3, NULL);
|
|
|
|
freq = atoi(argv[0]);
|
|
|
|
if (!strncasecmp(argv[1], "0x", 2))
|
|
level = strtol(argv[1], NULL, 16);
|
|
else
|
|
level = atoi(argv[1]);
|
|
|
|
test = atoi(argv[2]);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = test & 0xff;
|
|
array[1] = test >> 8;
|
|
array[2] = freq & 0xff;
|
|
array[3] = freq >> 8;
|
|
array[4] = level & 0xff;
|
|
array[5] = level >> 8;
|
|
|
|
return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
|
|
}
|
|
|
|
static int cmd_memtypes(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t type, stores[4] = { 0x0001, 0x0002, 0x0004, 0x0008 };
|
|
int i, err;
|
|
|
|
OPT_HELP(0, NULL);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = stores[i] & 0xff;
|
|
array[1] = stores[i] >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS_MEMORY_TYPE, array, 8);
|
|
if (err < 0)
|
|
continue;
|
|
|
|
type = array[2] + (array[3] << 8);
|
|
|
|
printf("%s (0x%04x) = %s (%d)\n", storestostr(stores[i]),
|
|
stores[i], memorytostr(type), type);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct option pskey_options[] = {
|
|
{ "stores", 1, 0, 's' },
|
|
{ "reset", 0, 0, 'r' },
|
|
{ "help", 0, 0, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static int opt_pskey(int argc, char *argv[], uint16_t *stores, int *reset, int *help)
|
|
{
|
|
int opt;
|
|
|
|
while ((opt=getopt_long(argc, argv, "+s:rh", pskey_options, NULL)) != EOF) {
|
|
switch (opt) {
|
|
case 's':
|
|
if (!stores)
|
|
break;
|
|
if (!strcasecmp(optarg, "default"))
|
|
*stores = 0x0000;
|
|
else if (!strcasecmp(optarg, "implementation"))
|
|
*stores = 0x0001;
|
|
else if (!strcasecmp(optarg, "factory"))
|
|
*stores = 0x0002;
|
|
else if (!strcasecmp(optarg, "rom"))
|
|
*stores = 0x0004;
|
|
else if (!strcasecmp(optarg, "ram"))
|
|
*stores = 0x0008;
|
|
else if (!strcasecmp(optarg, "psi"))
|
|
*stores = 0x0001;
|
|
else if (!strcasecmp(optarg, "psf"))
|
|
*stores = 0x0002;
|
|
else if (!strcasecmp(optarg, "psrom"))
|
|
*stores = 0x0004;
|
|
else if (!strcasecmp(optarg, "psram"))
|
|
*stores = 0x0008;
|
|
else if (!strncasecmp(optarg, "0x", 2))
|
|
*stores = strtol(optarg, NULL, 16);
|
|
else
|
|
*stores = atoi(optarg);
|
|
break;
|
|
|
|
case 'r':
|
|
if (reset)
|
|
*reset = 1;
|
|
break;
|
|
|
|
case 'h':
|
|
if (help)
|
|
*help = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return optind;
|
|
}
|
|
|
|
#define OPT_PSKEY(min, max, stores, reset, help) \
|
|
opt_pskey(argc, argv, (stores), (reset), (help)); \
|
|
argc -= optind; argv += optind; optind = 0; \
|
|
OPT_RANGE((min), (max))
|
|
|
|
static int cmd_psget(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[128];
|
|
uint16_t pskey, length, value, stores = CSR_STORES_DEFAULT;
|
|
uint32_t val32;
|
|
int i, err, reset = 0;
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
OPT_PSKEY(1, 1, &stores, &reset, NULL);
|
|
|
|
if (strncasecmp(argv[0], "0x", 2)) {
|
|
pskey = atoi(argv[0]);
|
|
|
|
for (i = 0; storage[i].pskey; i++) {
|
|
if (strcasecmp(storage[i].str, argv[0]))
|
|
continue;
|
|
|
|
pskey = storage[i].pskey;
|
|
break;
|
|
}
|
|
} else
|
|
pskey = strtol(argv[0] + 2, NULL, 16);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = stores & 0xff;
|
|
array[3] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
length = array[2] + (array[3] << 8);
|
|
if (length + 6 > (int) sizeof(array) / 2)
|
|
return -EIO;
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = length & 0xff;
|
|
array[3] = length >> 8;
|
|
array[4] = stores & 0xff;
|
|
array[5] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
switch (length) {
|
|
case 1:
|
|
value = array[6] | (array[7] << 8);
|
|
printf("%s: 0x%04x (%d)\n", csr_pskeytostr(pskey), value, value);
|
|
break;
|
|
|
|
case 2:
|
|
val32 = array[8] | (array[9] << 8) | (array[6] << 16) | (array[7] << 24);
|
|
printf("%s: 0x%08x (%d)\n", csr_pskeytostr(pskey), val32, val32);
|
|
break;
|
|
|
|
default:
|
|
printf("%s:", csr_pskeytostr(pskey));
|
|
for (i = 0; i < length; i++)
|
|
printf(" 0x%02x%02x", array[(i * 2) + 6], array[(i * 2) + 7]);
|
|
printf("\n");
|
|
break;
|
|
}
|
|
|
|
if (reset)
|
|
transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int cmd_psset(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[128];
|
|
uint16_t pskey, length, value, stores = CSR_STORES_PSRAM;
|
|
uint32_t val32;
|
|
int i, err, reset = 0;
|
|
|
|
memset(array, 0, sizeof(array));
|
|
|
|
OPT_PSKEY(2, 81, &stores, &reset, NULL);
|
|
|
|
if (strncasecmp(argv[0], "0x", 2)) {
|
|
pskey = atoi(argv[0]);
|
|
|
|
for (i = 0; storage[i].pskey; i++) {
|
|
if (strcasecmp(storage[i].str, argv[0]))
|
|
continue;
|
|
|
|
pskey = storage[i].pskey;
|
|
break;
|
|
}
|
|
} else
|
|
pskey = strtol(argv[0] + 2, NULL, 16);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = stores & 0xff;
|
|
array[3] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
length = array[2] + (array[3] << 8);
|
|
if (length + 6 > (int) sizeof(array) / 2)
|
|
return -EIO;
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = length & 0xff;
|
|
array[3] = length >> 8;
|
|
array[4] = stores & 0xff;
|
|
array[5] = stores >> 8;
|
|
|
|
argc--;
|
|
argv++;
|
|
|
|
switch (length) {
|
|
case 1:
|
|
if (argc != 1) {
|
|
errno = E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
if (!strncasecmp(argv[0], "0x", 2))
|
|
value = strtol(argv[0] + 2, NULL, 16);
|
|
else
|
|
value = atoi(argv[0]);
|
|
|
|
array[6] = value & 0xff;
|
|
array[7] = value >> 8;
|
|
break;
|
|
|
|
case 2:
|
|
if (argc != 1) {
|
|
errno = E2BIG;
|
|
return -1;
|
|
}
|
|
|
|
if (!strncasecmp(argv[0], "0x", 2))
|
|
val32 = strtol(argv[0] + 2, NULL, 16);
|
|
else
|
|
val32 = atoi(argv[0]);
|
|
|
|
array[6] = (val32 & 0xff0000) >> 16;
|
|
array[7] = val32 >> 24;
|
|
array[8] = val32 & 0xff;
|
|
array[9] = (val32 & 0xff00) >> 8;
|
|
break;
|
|
|
|
default:
|
|
if (argc != length * 2) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < length * 2; i++)
|
|
if (!strncasecmp(argv[0], "0x", 2))
|
|
array[i + 6] = strtol(argv[i] + 2, NULL, 16);
|
|
else
|
|
array[i + 6] = atoi(argv[i]);
|
|
break;
|
|
}
|
|
|
|
err = transport_write(transport, CSR_VARID_PS, array, (length + 3) * 2);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (reset)
|
|
transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int cmd_psclr(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t pskey, stores = CSR_STORES_PSRAM;
|
|
int i, err, reset = 0;
|
|
|
|
OPT_PSKEY(1, 1, &stores, &reset, NULL);
|
|
|
|
if (strncasecmp(argv[0], "0x", 2)) {
|
|
pskey = atoi(argv[0]);
|
|
|
|
for (i = 0; storage[i].pskey; i++) {
|
|
if (strcasecmp(storage[i].str, argv[0]))
|
|
continue;
|
|
|
|
pskey = storage[i].pskey;
|
|
break;
|
|
}
|
|
} else
|
|
pskey = strtol(argv[0] + 2, NULL, 16);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = stores & 0xff;
|
|
array[3] = stores >> 8;
|
|
|
|
err = transport_write(transport, CSR_VARID_PS_CLR_STORES, array, 8);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (reset)
|
|
transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int cmd_pslist(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[8];
|
|
uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
|
|
int err, reset = 0;
|
|
|
|
OPT_PSKEY(0, 0, &stores, &reset, NULL);
|
|
|
|
while (1) {
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = stores & 0xff;
|
|
array[3] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
|
|
if (err < 0)
|
|
break;
|
|
|
|
pskey = array[4] + (array[5] << 8);
|
|
if (pskey == 0x0000)
|
|
break;
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = stores & 0xff;
|
|
array[3] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
|
|
if (err < 0)
|
|
continue;
|
|
|
|
length = array[2] + (array[3] << 8);
|
|
|
|
printf("0x%04x - %s (%d bytes)\n", pskey,
|
|
csr_pskeytostr(pskey), length * 2);
|
|
}
|
|
|
|
if (reset)
|
|
transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_psread(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[256];
|
|
uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
|
|
char *str, val[7];
|
|
int i, err, reset = 0;
|
|
|
|
OPT_PSKEY(0, 0, &stores, &reset, NULL);
|
|
|
|
while (1) {
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = stores & 0xff;
|
|
array[3] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
|
|
if (err < 0)
|
|
break;
|
|
|
|
pskey = array[4] + (array[5] << 8);
|
|
if (pskey == 0x0000)
|
|
break;
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = stores & 0xff;
|
|
array[3] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
|
|
if (err < 0)
|
|
continue;
|
|
|
|
length = array[2] + (array[3] << 8);
|
|
if (length + 6 > (int) sizeof(array) / 2)
|
|
continue;
|
|
|
|
memset(array, 0, sizeof(array));
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = length & 0xff;
|
|
array[3] = length >> 8;
|
|
array[4] = stores & 0xff;
|
|
array[5] = stores >> 8;
|
|
|
|
err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
|
|
if (err < 0)
|
|
continue;
|
|
|
|
str = csr_pskeytoval(pskey);
|
|
if (!strcasecmp(str, "UNKNOWN")) {
|
|
sprintf(val, "0x%04x", pskey);
|
|
str = NULL;
|
|
}
|
|
|
|
printf("// %s%s\n&%04x =", str ? "PSKEY_" : "",
|
|
str ? str : val, pskey);
|
|
for (i = 0; i < length; i++)
|
|
printf(" %02x%02x", array[(i * 2) + 7], array[(i * 2) + 6]);
|
|
printf("\n");
|
|
}
|
|
|
|
if (reset)
|
|
transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_psload(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[256];
|
|
uint16_t pskey, length, size, stores = CSR_STORES_PSRAM;
|
|
char *str, val[7];
|
|
int err, reset = 0;
|
|
|
|
OPT_PSKEY(1, 1, &stores, &reset, NULL);
|
|
|
|
psr_read(argv[0]);
|
|
|
|
memset(array, 0, sizeof(array));
|
|
size = sizeof(array) - 6;
|
|
|
|
while (psr_get(&pskey, array + 6, &size) == 0) {
|
|
str = csr_pskeytoval(pskey);
|
|
if (!strcasecmp(str, "UNKNOWN")) {
|
|
sprintf(val, "0x%04x", pskey);
|
|
str = NULL;
|
|
}
|
|
|
|
printf("Loading %s%s ... ", str ? "PSKEY_" : "",
|
|
str ? str : val);
|
|
fflush(stdout);
|
|
|
|
length = size / 2;
|
|
|
|
array[0] = pskey & 0xff;
|
|
array[1] = pskey >> 8;
|
|
array[2] = length & 0xff;
|
|
array[3] = length >> 8;
|
|
array[4] = stores & 0xff;
|
|
array[5] = stores >> 8;
|
|
|
|
err = transport_write(transport, CSR_VARID_PS, array, size + 6);
|
|
|
|
printf("%s\n", err < 0 ? "failed" : "done");
|
|
|
|
memset(array, 0, sizeof(array));
|
|
size = sizeof(array) - 6;
|
|
}
|
|
|
|
if (reset)
|
|
transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_pscheck(int transport, int argc, char *argv[])
|
|
{
|
|
uint8_t array[256];
|
|
uint16_t pskey, size;
|
|
int i;
|
|
|
|
OPT_HELP(1, NULL);
|
|
|
|
psr_read(argv[0]);
|
|
|
|
while (psr_get(&pskey, array, &size) == 0) {
|
|
printf("0x%04x =", pskey);
|
|
for (i = 0; i < size; i++)
|
|
printf(" 0x%02x", array[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct {
|
|
char *str;
|
|
int (*func)(int transport, int argc, char *argv[]);
|
|
char *arg;
|
|
char *doc;
|
|
} commands[] = {
|
|
{ "builddef", cmd_builddef, "", "Get build definitions" },
|
|
{ "keylen", cmd_keylen, "<handle>", "Get current crypt key length" },
|
|
{ "clock", cmd_clock, "", "Get local Bluetooth clock" },
|
|
{ "rand", cmd_rand, "", "Get random number" },
|
|
{ "chiprev", cmd_chiprev, "", "Get chip revision" },
|
|
{ "buildname", cmd_buildname, "", "Get the full build name" },
|
|
{ "panicarg", cmd_panicarg, "", "Get panic code argument" },
|
|
{ "faultarg", cmd_faultarg, "", "Get fault code argument" },
|
|
{ "coldreset", cmd_coldreset, "", "Perform cold reset" },
|
|
{ "warmreset", cmd_warmreset, "", "Perform warm reset" },
|
|
{ "disabletx", cmd_disabletx, "", "Disable TX on the device" },
|
|
{ "enabletx", cmd_enabletx, "", "Enable TX on the device" },
|
|
{ "singlechan",cmd_singlechan,"<channel>", "Lock radio on specific channel" },
|
|
{ "hoppingon", cmd_hoppingon, "", "Revert to channel hopping" },
|
|
{ "rttxdata1", cmd_rttxdata1, "<freq> <level>", "TXData1 radio test" },
|
|
{ "radiotest", cmd_radiotest, "<freq> <level> <id>", "Run radio tests" },
|
|
{ "memtypes", cmd_memtypes, NULL, "Get memory types" },
|
|
{ "psget", cmd_psget, "<key>", "Get value for PS key" },
|
|
{ "psset", cmd_psset, "<key> <value>", "Set value for PS key" },
|
|
{ "psclr", cmd_psclr, "<key>", "Clear value for PS key" },
|
|
{ "pslist", cmd_pslist, NULL, "List all PS keys" },
|
|
{ "psread", cmd_psread, NULL, "Read all PS keys" },
|
|
{ "psload", cmd_psload, "<file>", "Load all PS keys from PSR file" },
|
|
{ "pscheck", cmd_pscheck, "<file>", "Check PSR file" },
|
|
{ NULL }
|
|
};
|
|
|
|
static void usage(void)
|
|
{
|
|
int i, pos = 0;
|
|
|
|
printf("bccmd - Utility for the CSR BCCMD interface\n\n");
|
|
printf("Usage:\n"
|
|
"\tbccmd [options] <command>\n\n");
|
|
|
|
printf("Options:\n"
|
|
"\t-t <transport> Select the transport\n"
|
|
"\t-d <device> Select the device\n"
|
|
"\t-b <bcsp rate> Select the bcsp transfer rate\n"
|
|
"\t-h, --help Display help\n"
|
|
"\n");
|
|
|
|
printf("Transports:\n"
|
|
"\tHCI USB BCSP H4 3WIRE\n\n");
|
|
|
|
printf("Commands:\n");
|
|
for (i = 0; commands[i].str; i++)
|
|
printf("\t%-10s %-20s\t%s\n", commands[i].str,
|
|
commands[i].arg ? commands[i].arg : " ",
|
|
commands[i].doc);
|
|
printf("\n");
|
|
|
|
printf("Keys:\n\t");
|
|
for (i = 0; storage[i].pskey; i++) {
|
|
printf("%s ", storage[i].str);
|
|
pos += strlen(storage[i].str) + 1;
|
|
if (pos > 60) {
|
|
printf("\n\t");
|
|
pos = 0;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static struct option main_options[] = {
|
|
{ "transport", 1, 0, 't' },
|
|
{ "device", 1, 0, 'd' },
|
|
{ "bcsprate", 1, 0, 'b'},
|
|
{ "help", 0, 0, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char *device = NULL;
|
|
int i, err, opt, transport = CSR_TRANSPORT_HCI;
|
|
speed_t bcsp_rate = B38400;
|
|
|
|
while ((opt=getopt_long(argc, argv, "+t:d:i:b:h", main_options, NULL)) != EOF) {
|
|
switch (opt) {
|
|
case 't':
|
|
if (!strcasecmp(optarg, "hci"))
|
|
transport = CSR_TRANSPORT_HCI;
|
|
else if (!strcasecmp(optarg, "usb"))
|
|
transport = CSR_TRANSPORT_USB;
|
|
else if (!strcasecmp(optarg, "bcsp"))
|
|
transport = CSR_TRANSPORT_BCSP;
|
|
else if (!strcasecmp(optarg, "h4"))
|
|
transport = CSR_TRANSPORT_H4;
|
|
else if (!strcasecmp(optarg, "h5"))
|
|
transport = CSR_TRANSPORT_3WIRE;
|
|
else if (!strcasecmp(optarg, "3wire"))
|
|
transport = CSR_TRANSPORT_3WIRE;
|
|
else if (!strcasecmp(optarg, "twutl"))
|
|
transport = CSR_TRANSPORT_3WIRE;
|
|
else
|
|
transport = CSR_TRANSPORT_UNKNOWN;
|
|
break;
|
|
|
|
case 'd':
|
|
case 'i':
|
|
device = strdup(optarg);
|
|
break;
|
|
case 'b':
|
|
switch (atoi(optarg)) {
|
|
case 9600: bcsp_rate = B9600; break;
|
|
case 19200: bcsp_rate = B19200; break;
|
|
case 38400: bcsp_rate = B38400; break;
|
|
case 57600: bcsp_rate = B57600; break;
|
|
case 115200: bcsp_rate = B115200; break;
|
|
case 230400: bcsp_rate = B230400; break;
|
|
case 460800: bcsp_rate = B460800; break;
|
|
case 500000: bcsp_rate = B500000; break;
|
|
case 576000: bcsp_rate = B576000; break;
|
|
case 921600: bcsp_rate = B921600; break;
|
|
case 1000000: bcsp_rate = B1000000; break;
|
|
case 1152000: bcsp_rate = B1152000; break;
|
|
case 1500000: bcsp_rate = B1500000; break;
|
|
case 2000000: bcsp_rate = B2000000; break;
|
|
#ifdef B2500000
|
|
case 2500000: bcsp_rate = B2500000; break;
|
|
#endif
|
|
#ifdef B3000000
|
|
case 3000000: bcsp_rate = B3000000; break;
|
|
#endif
|
|
#ifdef B3500000
|
|
case 3500000: bcsp_rate = B3500000; break;
|
|
#endif
|
|
#ifdef B4000000
|
|
case 4000000: bcsp_rate = B4000000; break;
|
|
#endif
|
|
default:
|
|
printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n");
|
|
bcsp_rate = B38400;
|
|
}
|
|
break;
|
|
case 'h':
|
|
default:
|
|
usage();
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
optind = 0;
|
|
|
|
if (argc < 1) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
if (transport_open(transport, device, bcsp_rate) < 0)
|
|
exit(1);
|
|
|
|
if (device)
|
|
free(device);
|
|
|
|
for (i = 0; commands[i].str; i++) {
|
|
if (strcasecmp(commands[i].str, argv[0]))
|
|
continue;
|
|
|
|
err = commands[i].func(transport, argc, argv);
|
|
|
|
transport_close(transport);
|
|
|
|
if (err < 0) {
|
|
fprintf(stderr, "Can't execute command: %s (%d)\n",
|
|
strerror(errno), errno);
|
|
exit(1);
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
|
|
fprintf(stderr, "Unsupported command\n");
|
|
|
|
transport_close(transport);
|
|
|
|
exit(1);
|
|
}
|