bluetooth: backend-native: add battery level reporting

Devices for Apple's iOS uses a few extra HFP AT commands to
inform the iPhone about the headphone's battery status.
Apple documented the AT commands in the following document:

https://developer.apple.com/hardwaredrivers/BluetoothDesignGuidelines.pdf

The patch has been tested with a Bose QC35, which results
in the following communication:

D: [pulseaudio] backend-native.c: RFCOMM << AT+VGS=14
D: [pulseaudio] backend-native.c: RFCOMM >> OK
D: [pulseaudio] backend-native.c: RFCOMM << AT+XAPL=009E-400C-0129,3
D: [pulseaudio] backend-native.c: RFCOMM >> +XAPL=iPhone,2
D: [pulseaudio] backend-native.c: RFCOMM >> OK
D: [pulseaudio] backend-native.c: RFCOMM << AT+XEVENT=Bose SoundLink,158
D: [pulseaudio] backend-native.c: RFCOMM >> OK
D: [pulseaudio] backend-native.c: RFCOMM << AT+IPHONEACCEV=2,1,4,2,0
N: [pulseaudio] backend-native.c: Battery Level: 50%
N: [pulseaudio] backend-native.c: Dock Status: undocked
D: [pulseaudio] backend-native.c: RFCOMM >> OK

[Marijn: Adapt for recent HSP/HFP code changes]

Co-authored-by: Marijn Suijten <marijns95@gmail.com>
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/482>
This commit is contained in:
Sebastian Reichel 2017-02-12 23:42:00 +01:00 committed by Marijn Suijten
parent 66e2672360
commit 7a84a24652

View file

@ -694,6 +694,8 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
ssize_t len;
int gain, dummy;
bool do_reply = false;
int vendor, product, version, features;
int num;
len = pa_read(fd, buf, 511, NULL);
if (len < 0) {
@ -734,6 +736,31 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
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,2");
do_reply = true;
} else if (sscanf(buf, "AT+IPHONEACCEV=%d", &num) == 1) {
char *substr = strchr(buf, ',');
bool isval = false;
int key, val;
for (; substr; substr = strchr(substr, ',')) {
substr++;
if (!isval) {
key = atoi(substr);
} else {
val = atoi(substr);
if (key == 1) {
pa_log_notice("Battery Level: %d0%%", val + 1);
} else if (key == 2) {
pa_log_notice("Dock Status: %s", val ? "docked" : "undocked");
}
}
isval = !isval;
}
do_reply = true;
} else if (t->config) { /* t->config is only non-null for hfp profile */
do_reply = hfp_rfcomm_handle(fd, t, buf);
} else {