diff options
Diffstat (limited to 'tools/bccmd.c')
| -rw-r--r-- | tools/bccmd.c | 1218 | 
1 files changed, 1218 insertions, 0 deletions
diff --git a/tools/bccmd.c b/tools/bccmd.c new file mode 100644 index 00000000..95cb37bb --- /dev/null +++ b/tools/bccmd.c @@ -0,0 +1,1218 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2004-2008  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) +{ +	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); +	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]; +	int i, 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 > 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 > 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 > 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-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' }, +	{ "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; + +	while ((opt=getopt_long(argc, argv, "+t:d:i: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 'h': +		default: +			usage(); +			exit(0); +		} +	} + +	argc -= optind; +	argv += optind; +	optind = 0; + +	if (argc < 1) { +		usage(); +		exit(1); +	} + +	if (transport_open(transport, device) < 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); +}  | 
