diff --git a/android/handsfree.c b/android/handsfree.c
index 04921ba..679c39f 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
#define HFP_AG_FEATURES ( HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\
HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\
- HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR )
+ HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR |\
+ HFP_AG_FEAT_CODEC )
#define HFP_AG_CHLD "0,1,2,3"
#define RING_TIMEOUT 2
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
struct indicator {
const char *name;
int min;
bool active;
};
+struct hfp_codec {
+ uint8_t type;
+ bool local_supported;
+ bool remote_supported;
+};
+
static const struct indicator inds_defaults[] = {
{ "service", 0, 1, 0, false, true },
{ "call", 0, 1, 0, true, true },
{ "battchg", 0, 5, 0, false, true },
};
+static const struct hfp_codec codecs_defaults[] = {
+ { CODEC_ID_CVSD, true, false},
+ { CODEC_ID_MSBC, false, false},
+};
+
static struct {
bdaddr_t bdaddr;
uint8_t state;
bool ccwa_enabled;
bool indicators_enabled;
struct indicator inds[IND_COUNT];
+ uint8_t negotiated_codec;
+ uint8_t proposed_codec;
+ struct hfp_codec codecs[CODECS_COUNT];
guint ring;
bool hsp;
struct hfp_gw *gw;
memcpy(device.inds, inds_defaults, sizeof(device.inds));
+ memcpy(device.codecs, codecs_defaults, sizeof(device.codecs));
+
device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
}
switch (type) {
case HFP_GW_CMD_TYPE_TEST:
+ /* If device supports Codec Negotiation, AT+BAC should be
+ * received first
+ */
+ if ((device.features & HFP_HF_FEAT_CODEC))
+ if (!device.codecs[CVSD_OFFSET].remote_supported)
+ break;
+
len = strlen("+CIND:") + 1;
for (i = 0; i < IND_COUNT; i++) {
hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
}
+static struct hfp_codec *find_codec_by_type(uint8_t type)
+{
+ int i;
+
+ for (i = 0; i < CODECS_COUNT; i++)
+ if (type == device.codecs[i].type)
+ return &device.codecs[i];
+
+ return NULL;
+}
+
static void at_cmd_bac(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
void *user_data)
{
+ unsigned int val;
+
DBG("");
- /* TODO */
+ switch (type) {
+ case HFP_GW_CMD_TYPE_SET:
+ if (!(device.features & HFP_HF_FEAT_CODEC))
+ goto failed;
+
+ /* Clear list of codecs */
+ memcpy(device.codecs, codecs_defaults, sizeof(device.codecs));
+ device.negotiated_codec = 0;
+
+ /* At least CVSD mandatory codec must exist
+ * HFP V1.6 4.34.1
+ */
+ if (!hfp_gw_result_get_number(result, &val)
+ || val != CODEC_ID_CVSD)
+ goto failed;
+ device.codecs[CVSD_OFFSET].remote_supported = true;
+
+ if (hfp_gw_result_get_number(result, &val)) {
+ if (val != CODEC_ID_MSBC)
+ goto failed;
+
+ device.codecs[MSBC_OFFSET].remote_supported = true;
+ }
+
+ while (hfp_gw_result_has_next(result)) {
+ struct hfp_codec *codec;
+
+ if (!hfp_gw_result_get_number(result, &val))
+ goto failed;
+
+ codec = find_codec_by_type(val);
+ if (!codec)
+ continue;
+
+ codec->remote_supported = true;
+ }
+
+ hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+ return;
+ case HFP_GW_CMD_TYPE_TEST:
+ case HFP_GW_CMD_TYPE_READ:
+ case HFP_GW_CMD_TYPE_COMMAND:
+ break;
+ }
+
+failed:
hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
}