/* Copyright 2016 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "bluetooth_le.h"
#include "util.h"
#include "console.h"

#define CPRINTF(format, args...) cprintf(CC_BLUETOOTH_LE, format, ## args)

/*
 * Convert from BLE Channel to frequency
 *
 * Bluetooth 4.1 Vol 6 pg 36 4.1 Table 1.1
 */

#define CHAN_0_MHZ      2404
#define CHAN_11_MHZ     2428
#define CHAN_37_MHZ     2402
#define CHAN_38_MHZ     2426
#define CHAN_39_MHZ     2480

int chan2freq(int channel)
{
	int freq;

	ASSERT(channel < 40 && channel >= 0);

	switch (channel) {
	case 37: /* Advertising */
		freq = CHAN_37_MHZ;
		break;
	case 38: /* Advertising */
		freq = CHAN_38_MHZ;
		break;
	case 39: /* Advertising */
		freq = CHAN_39_MHZ;
		break;
	default:
		/* Data Channels */
		if (channel < 11)
			freq = channel * 2 + CHAN_0_MHZ;
		else
			freq = (channel - 11) * 2 + CHAN_11_MHZ;
	}
	return freq;
}

/* BLE 4.1 Vol 6 2.3.3.1 */

void fill_remapping_table(struct remapping_table *rt, uint8_t map[5],
			  int hop_increment)
{
	int i;

	rt->num_used_channels = 0;
	rt->last_unmapped_channel = 0;
	rt->hop_increment = hop_increment;

	for (i = 0; i < 37; i++)
		if (map[i / 8] & (1 << (i % 8)))
			rt->remapping_index[rt->num_used_channels++] = i;
	memcpy(rt->map, map, sizeof(rt->map));
}

/* BLE 4.1 Vol 6 4.5.8 */
uint8_t get_next_data_channel(struct remapping_table *rt)
{
	rt->last_unmapped_channel =
		(rt->last_unmapped_channel + rt->hop_increment) % 37;

	/* Check if the channel is mapped */
	if (rt->map[rt->last_unmapped_channel / 8] &
			(1 << (rt->last_unmapped_channel % 8)))
		return rt->last_unmapped_channel;
	else
		return rt->remapping_index
			[rt->last_unmapped_channel % rt->num_used_channels];
}

/* BLE 4.1 Vol 3 Part C 11 */

/* Pack advertising structures for sending */
uint8_t *pack_adv(uint8_t *dest, int length, int type, const uint8_t *data)
{
	/* Add the structure length */
	dest[0] = (uint8_t)length+1;
	/* Add the structure type */
	dest[1] = (uint8_t)type;
	/* Add the data */
	memcpy(&dest[2], data, length);

	/* Return a pointer to the next structure */
	return &dest[2+length];
}

uint8_t *pack_adv_int(uint8_t *dest, int length, int type, int data)
{
	/* Add the structure length */
	dest[0] = (uint8_t)length+1;
	/* Add the structure type */
	dest[1] = (uint8_t)type;
	/* Add the data */
	memcpy(&dest[2], &data, length);

	/* Return a pointer to the next structure */
	return &dest[2+length];
}

uint8_t *pack_adv_addr(uint8_t *dest, uint64_t addr)
{
	memcpy(&dest[0], &addr, BLUETOOTH_ADDR_OCTETS);

	/* Return a pointer to the next structure */
	return &dest[BLUETOOTH_ADDR_OCTETS];
}

/* Parse advertising structures that have been received */
const uint8_t *unpack_adv(const uint8_t *src, int *length, int *type,
			  const uint8_t **data)
{
	/* Get the structure length */
	*length = *(src++);
	/* Get the structure type */
	*type = *(src++);
	/* Get the data */
	*data = src;

	/* Return a pointer to the next structure */
	return src + *length;
}

static void mem_dump(uint8_t *mem, int len)
{
	int i;
	uint8_t value;

	for (i = 0; i < len; i++) {
		value = mem[i];
		if (i % 8 == 0)
			CPRINTF("\n%08x: %02x", &mem[i], value);
		else
			CPRINTF(" %02x", value);
	}
	CPRINTF("\n");
}

void dump_ble_addr(uint8_t *mem, char *name)
{
	int i;

	for (i = 5; i > 0; i--)
		CPRINTF("%02x.", mem[i]);
	CPRINTF("%02x %s\n", mem[0], name);
}

void dump_ble_packet(struct ble_pdu *ble_p)
{
	int curr_offs;

	if (ble_p->header_type_adv) {
		CPRINTF("BLE packet @ %p: type %d, len %d, %s %s\n",
			ble_p, ble_p->header.adv.type, ble_p->header.adv.length,
			(ble_p->header.adv.txaddr ? " TXADDR" : ""),
			(ble_p->header.adv.rxaddr ? " RXADDR" : ""));

		curr_offs = 0;

		if (ble_p->header.adv.type ==
			BLE_ADV_HEADER_PDU_TYPE_SCAN_REQ) {
			dump_ble_addr(ble_p->payload, "ScanA");
			curr_offs += BLUETOOTH_ADDR_OCTETS;
		} else if (ble_p->header.adv.type ==
			BLE_ADV_HEADER_PDU_TYPE_CONNECT_REQ) {
			dump_ble_addr(ble_p->payload, "InitA");
			curr_offs += BLUETOOTH_ADDR_OCTETS;
		}
		/* All packets have AdvA */
		dump_ble_addr(ble_p->payload + curr_offs, "AdvA");
		curr_offs += BLUETOOTH_ADDR_OCTETS;

		if (ble_p->header.adv.type ==
		    BLE_ADV_HEADER_PDU_TYPE_ADV_DIRECT_IND)
			dump_ble_addr(ble_p->payload + curr_offs, "InitA");
		else
			mem_dump(ble_p->payload + curr_offs,
				 ble_p->header.adv.length - curr_offs);
	} else { /* Data PDUs */
		CPRINTF("BLE data packet @%p: LLID %d,"
			" nesn %d, sn %d, md %d, length %d\n",
			ble_p, ble_p->header.data.llid, ble_p->header.data.nesn,
			ble_p->header.data.sn, ble_p->header.data.md,
			ble_p->header.data.length);
		mem_dump(ble_p->payload, ble_p->header.data.length);
	}
}

