[Git][pulseaudio/pulseaudio][master] backend-native: Handle multi AT commands in a buffer

PulseAudio Marge Bot (@pulseaudio-merge-bot) gitlab at gitlab.freedesktop.org
Fri Jan 12 16:20:52 UTC 2024



PulseAudio Marge Bot pushed to branch master at PulseAudio / pulseaudio


Commits:
d7dc04e8 by Hui Wang at 2024-01-05T23:12:18+08:00
backend-native: Handle multi AT commands in a buffer

When we connect Lenovo XT99 bt headset in the Ubuntu 22.04, this
headset could only work in A2DP profile, couldn't work in HFP profile
with a high chance.

This headset supports mSBC, after pulseaudio replies "+BCS:2" to
headset, we expect to receive a "AT+BCS=2\r" from the headset, but
with a high chance, it will receive 2 AT commands in a buffer like
this "AT+CHLD=?\rAT+BCS=2\r", and we also observed other 2 AT commands
in a buffer like this "AT+NREC=0\rAT+CGMI?\r".

Here we don't suppose there is only one AT command in a buffer, we
will find each command by the delimiter "\r" and handle each command
by sequence.

Signed-off-by: Hui Wang <hui.wang at canonical.com>
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/804>

- - - - -


1 changed file:

- src/modules/bluetooth/backend-native.c


Changes:

=====================================
src/modules/bluetooth/backend-native.c
=====================================
@@ -889,106 +889,120 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
     }
 
     if (events & PA_IO_EVENT_INPUT) {
-        char buf[512];
+        char rbuf[512];
         ssize_t len;
         int gain, dummy;
         bool do_reply = false;
         int vendor, product, version, features;
+        char *buf = rbuf;
         int num;
 
-        len = pa_read(fd, buf, 511, NULL);
+        len = pa_read(fd, rbuf, 511, NULL);
         if (len < 0) {
             pa_log_error("RFCOMM read error: %s", pa_cstrerror(errno));
             goto fail;
         }
-        buf[len] = 0;
-        pa_log_debug("RFCOMM << %s", buf);
-
-        /* There are only four HSP AT commands:
-         * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain.
-         * +VGS=value is sent by AG to HS as a response to an AT+VGS command or when the gain
-         * is changed on the AG side.
-         * AT+VGM=value: value between 0 and 15, sent by the HS to AG to set the microphone gain.
-         * +VGM=value is sent by AG to HS as a response to an AT+VGM command or when the gain
-         * is changed on the AG side.
-         * AT+CKPD=200: Sent by HS when headset button is pressed.
-         * 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, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM%*[=:]%d\r\n", &gain) == 1) {
-            if (!t->set_sink_volume) {
-                pa_log_debug("HS/HF peer supports speaker gain control");
-                t->set_sink_volume = set_sink_volume;
-            }
+        rbuf[len] = 0;
+        pa_log_debug("RFCOMM << %s", rbuf);
+
+        while (buf[0]) {
+            /* There are only four HSP AT commands:
+             * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain.
+             * +VGS=value is sent by AG to HS as a response to an AT+VGS command or when the gain
+             * is changed on the AG side.
+             * AT+VGM=value: value between 0 and 15, sent by the HS to AG to set the microphone gain.
+             * +VGM=value is sent by AG to HS as a response to an AT+VGM command or when the gain
+             * is changed on the AG side.
+             * AT+CKPD=200: Sent by HS when headset button is pressed.
+             * 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, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM%*[=:]%d\r\n", &gain) == 1) {
+                if (!t->set_sink_volume) {
+                    pa_log_debug("HS/HF peer supports speaker gain control");
+                    t->set_sink_volume = set_sink_volume;
+                }
 
-            t->sink_volume = hsp_gain_to_volume(gain);
-            pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED), t);
-            do_reply = true;
+                t->sink_volume = hsp_gain_to_volume(gain);
+                pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED), t);
+                do_reply = true;
 
-        } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS%*[=:]%d\r\n", &gain) == 1) {
-            if (!t->set_source_volume) {
-                pa_log_debug("HS/HF peer supports microphone gain control");
-                t->set_source_volume = set_source_volume;
-            }
-
-            t->source_volume = hsp_gain_to_volume(gain);
-            pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), t);
-            do_reply = true;
-        } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
-            do_reply = true;
-        } else if (sscanf(buf, "AT+XAPL=%04x-%04x-%04x,%d", &vendor, &product, &version, &features) == 4) {
-            if (features & 0x2)
-                /* claim, that we support battery status reports */
-                rfcomm_write_response(fd, "+XAPL=iPhone,6");
-            do_reply = true;
-        } else if (sscanf(buf, "AT+IPHONEACCEV=%d", &num) == 1) {
-            char *substr = buf, *keystr;
-            int key, val, i;
-
-            do_reply = true;
-
-            for (i = 0; i < num; ++i) {
-                keystr = strchr(substr, ',');
-                if (!keystr) {
-                    pa_log_warn("%s misses key for argument #%d", buf, i);
-                    do_reply = false;
-                    break;
-                }
-                keystr++;
-                substr = strchr(keystr, ',');
-                if (!substr) {
-                    pa_log_warn("%s misses value for argument #%d", buf, i);
-                    do_reply = false;
-                    break;
+            } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS%*[=:]%d\r\n", &gain) == 1) {
+                if (!t->set_source_volume) {
+                    pa_log_debug("HS/HF peer supports microphone gain control");
+                    t->set_source_volume = set_source_volume;
                 }
-                substr++;
 
-                key = atoi(keystr);
-                val = atoi(substr);
-
-                switch (key) {
-                    case 1:
-                        pa_log_notice("Battery Level: %d0%%", val + 1);
-                        pa_bluetooth_device_report_battery_level(t->device, (val + 1) * 10, "Apple accessory indication");
-                        break;
-                    case 2:
-                        pa_log_notice("Dock Status: %s", val ? "docked" : "undocked");
+                t->source_volume = hsp_gain_to_volume(gain);
+                pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), t);
+                do_reply = true;
+            } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
+                do_reply = true;
+            } else if (sscanf(buf, "AT+XAPL=%04x-%04x-%04x,%d", &vendor, &product, &version, &features) == 4) {
+                if (features & 0x2)
+                    /* claim, that we support battery status reports */
+                    rfcomm_write_response(fd, "+XAPL=iPhone,6");
+                do_reply = true;
+            } else if (sscanf(buf, "AT+IPHONEACCEV=%d", &num) == 1) {
+                char *substr = buf, *keystr;
+                int key, val, i;
+
+                do_reply = true;
+
+                for (i = 0; i < num; ++i) {
+                    keystr = strchr(substr, ',');
+                    if (!keystr) {
+                        pa_log_warn("%s misses key for argument #%d", buf, i);
+                        do_reply = false;
                         break;
-                    default:
-                        pa_log_debug("Unexpected IPHONEACCEV key %#x", key);
+                    }
+                    keystr++;
+                    substr = strchr(keystr, ',');
+                    if (!substr) {
+                        pa_log_warn("%s misses value for argument #%d", buf, i);
+                        do_reply = false;
                         break;
+                    }
+                    substr++;
+
+                    key = atoi(keystr);
+                    val = atoi(substr);
+
+                    switch (key) {
+                        case 1:
+                            pa_log_notice("Battery Level: %d0%%", val + 1);
+                            pa_bluetooth_device_report_battery_level(t->device, (val + 1) * 10, "Apple accessory indication");
+                            break;
+                        case 2:
+                            pa_log_notice("Dock Status: %s", val ? "docked" : "undocked");
+                            break;
+                        default:
+                            pa_log_debug("Unexpected IPHONEACCEV key %#x", key);
+                            break;
+                    }
                 }
-            }
-            if (!do_reply)
+                if (!do_reply)
+                    rfcomm_write_response(fd, "ERROR");
+            } else if (t->config) { /* t->config is only non-null for hfp profile */
+                do_reply = hfp_rfcomm_handle(fd, t, buf);
+            } else {
                 rfcomm_write_response(fd, "ERROR");
-        } else if (t->config) { /* t->config is only non-null for hfp profile */
-            do_reply = hfp_rfcomm_handle(fd, t, buf);
-        } else {
-            rfcomm_write_response(fd, "ERROR");
-            do_reply = false;
-        }
+                do_reply = false;
+            }
 
-        if (do_reply)
-            rfcomm_write_response(fd, "OK");
+            if (do_reply)
+                rfcomm_write_response(fd, "OK");
+
+            if (buf[0] == '\r') /* in case it is the command with format \r\nCOMMAND\r\n, skip the starting \r */
+                buf = buf + 1;
+
+            buf = strstr(buf, "\r"); /* try to find the next AT command in the buf */
+            if (!buf)
+                break;
+            else if (buf[1] == '\n')
+                buf = buf + 2; /* skip \r\n */
+            else
+                buf = buf + 1; /* skip \r */
+        }
     }
 
     return;



View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/commit/d7dc04e8f5c404b1fa16409f69dcde7c56312f02

-- 
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/commit/d7dc04e8f5c404b1fa16409f69dcde7c56312f02
You're receiving this email because of your account on gitlab.freedesktop.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20240112/c82b1f84/attachment-0001.htm>


More information about the pulseaudio-commits mailing list