mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	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:
		
							parent
							
								
									3da66734bd
								
							
						
					
					
						commit
						b533b06b51
					
				
					 1 changed files with 171 additions and 155 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue