bluez5: backend-native: handle multiple commands in RFCOMM input

Do relaxed parsing of RFCOMM commands for AG & HF roles, allowing
multiple commands in same buffer.

Use same parser code for all HFP/HSP AG/HF. Parse input in relaxed way,
as some devices emit spurious \n
This commit is contained in:
Pauli Virtanen 2024-01-04 14:21:46 +02:00
parent 3da66734bd
commit b533b06b51

View file

@ -496,19 +496,19 @@ static bool rfcomm_hsp_hs(struct rfcomm *rfcomm, char* buf)
* or when the gain is changed on the AG side.
* RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because
* it does not expect a reply. */
if (sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) {
if (sscanf(buf, "+VGS=%d", &gain) == 1) {
if (gain <= SPA_BT_VOLUME_HS_MAX) {
rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_RX, gain);
} else {
spa_log_debug(backend->log, "RFCOMM receive unsupported VGS gain: %s", buf);
}
} else if (sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) {
} else if (sscanf(buf, "+VGM=%d", &gain) == 1) {
if (gain <= SPA_BT_VOLUME_HS_MAX) {
rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_TX, gain);
} else {
spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", buf);
}
} else if (spa_strstartswith(buf, "\r\nOK\r\n")) {
} else if (spa_streq(buf, "OK")) {
if (rfcomm->hs_state == hsp_hs_init2) {
if (rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX))
rfcomm->hs_state = hsp_hs_vgs;
@ -777,12 +777,6 @@ static bool rfcomm_hfp_ag(struct rfcomm *rfcomm, char* buf)
int xapl_product;
int xapl_features;
spa_debug_log_mem(backend->log, SPA_LOG_LEVEL_DEBUG, 2, buf, strlen(buf));
/* Some devices send initial \n: be permissive */
while (*buf == '\n')
++buf;
if (sscanf(buf, "AT+BRSF=%u", &features) == 1) {
unsigned int ag_features = SPA_BT_HFP_AG_FEATURE_NONE;
@ -884,7 +878,7 @@ static bool rfcomm_hfp_ag(struct rfcomm *rfcomm, char* buf)
rfcomm_emit_volume_changed(rfcomm, -1, SPA_BT_VOLUME_INVALID);
}
}
} else if (spa_streq(buf, "\r")) {
} else if (spa_streq(buf, "")) {
/* No commands, reply OK (ITU-T Rec. V.250 Sec. 5.2.1 & 5.6) */
rfcomm_send_reply(rfcomm, "OK");
} else if (!rfcomm->slc_configured) {
@ -1178,13 +1172,11 @@ next_indicator:
return true;
}
static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* buf)
static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token)
{
struct impl *backend = rfcomm->backend;
unsigned int features, gain, selected_codec, indicator, value;
char* token;
while ((token = strsep(&buf, "\r\n"))) {
if (sscanf(token, "+BRSF:%u", &features) == 1) {
if (((features & (SPA_BT_HFP_AG_FEATURE_CODEC_NEGOTIATION)) != 0) &&
rfcomm->msbc_supported_by_hfp)
@ -1320,13 +1312,41 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* buf)
break;
}
}
}
return true;
}
#endif
static void rfcomm_process_events(struct rfcomm *rfcomm, char *buf, bool ag, bool (*handler)(struct rfcomm *, char *))
{
struct impl *backend = rfcomm->backend;
char *token;
/* Relaxed parsing of both <COMMAND>\r (AG) and \r\n<REPLY>\r\n (HF) */
while ((token = strsep(&buf, "\r"))) {
size_t len;
/* Skip leading and trailing \n */
while (*token == '\n')
++token;
for (len = strlen(token); len > 0 && token[len - 1] == '\n'; --len)
token[len - 1] = '\0';
/* Skip empty (only last one if AG) */
if (*token == '\0' && (buf == NULL || !ag))
continue;
spa_log_debug(backend->log, "RFCOMM event: %s", token);
if (!handler(rfcomm, token)) {
spa_log_debug(backend->log, "RFCOMM received unsupported event: %s", token);
rfcomm_send_error(rfcomm, CMEE_OPERATION_NOT_SUPPORTED);
}
}
}
static void rfcomm_event(struct spa_source *source)
{
struct rfcomm *rfcomm = source->data;
@ -1341,41 +1361,37 @@ static void rfcomm_event(struct spa_source *source)
if (source->rmask & SPA_IO_IN) {
char buf[512];
ssize_t len;
bool res = false;
len = read(source->fd, buf, 511);
len = read(source->fd, buf, sizeof(buf) - 1);
if (len < 0) {
spa_log_error(backend->log, "RFCOMM read error: %s", strerror(errno));
return;
}
buf[len] = 0;
spa_log_debug(backend->log, "RFCOMM << %s", buf);
spa_debug_log_mem(backend->log, SPA_LOG_LEVEL_DEBUG, 2, buf, strlen(buf));
switch (rfcomm->profile) {
#ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE
case SPA_BT_PROFILE_HSP_HS:
res = rfcomm_hsp_ag(rfcomm, buf);
rfcomm_process_events(rfcomm, buf, true, rfcomm_hsp_ag);
break;
case SPA_BT_PROFILE_HSP_AG:
res = rfcomm_hsp_hs(rfcomm, buf);
rfcomm_process_events(rfcomm, buf, false, rfcomm_hsp_hs);
break;
#endif
#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
case SPA_BT_PROFILE_HFP_HF:
res = rfcomm_hfp_ag(rfcomm, buf);
rfcomm_process_events(rfcomm, buf, true, rfcomm_hfp_ag);
break;
case SPA_BT_PROFILE_HFP_AG:
res = rfcomm_hfp_hf(rfcomm, buf);
rfcomm_process_events(rfcomm, buf, false, rfcomm_hfp_hf);
break;
#endif
default:
break;
}
if (!res) {
spa_log_debug(backend->log, "RFCOMM received unsupported command: %s", buf);
rfcomm_send_error(rfcomm, CMEE_OPERATION_NOT_SUPPORTED);
}
}
}