diff -Nru bluez-5.71/ChangeLog bluez-5.72/ChangeLog --- bluez-5.71/ChangeLog 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/ChangeLog 2024-01-13 06:25:53.000000000 +0800 @@ -1,3 +1,11 @@ +ver 5.72: + Fix issue with BAP and handling stream IO linking. + Fix issue with BAP and setup of multiple streams per endpoint. + Fix issue with AVDTP and potential incorrect transaction label. + Fix issue with A2DP and handling crash on suspend. + Fix issue with GATT database and an invalid pointer. + Add support for AICS service. + ver 5.71: Fix issue with not registering CSIS service. Fix issue with registering pairing callbacks. diff -Nru bluez-5.71/client/agent.c bluez-5.72/client/agent.c --- bluez-5.71/client/agent.c 2023-08-25 01:02:39.000000000 +0800 +++ bluez-5.72/client/agent.c 2024-01-13 06:25:53.000000000 +0800 @@ -77,14 +77,17 @@ { DBusConnection *conn = user_data; - if (!strcmp(input, "yes")) - g_dbus_send_reply(conn, pending_message, DBUS_TYPE_INVALID); - else if (!strcmp(input, "no")) - g_dbus_send_error(conn, pending_message, + if (pending_message != NULL) { + if (!strcmp(input, "yes")) + g_dbus_send_reply(conn, pending_message, + DBUS_TYPE_INVALID); + else if (!strcmp(input, "no")) + g_dbus_send_error(conn, pending_message, "org.bluez.Error.Rejected", NULL); - else - g_dbus_send_error(conn, pending_message, + else + g_dbus_send_error(conn, pending_message, "org.bluez.Error.Canceled", NULL); + } } static void agent_release(DBusConnection *conn) diff -Nru bluez-5.71/client/bluetoothctl.1 bluez-5.72/client/bluetoothctl.1 --- bluez-5.71/client/bluetoothctl.1 1970-01-01 08:00:00.000000000 +0800 +++ bluez-5.72/client/bluetoothctl.1 2024-01-13 06:43:02.000000000 +0800 @@ -0,0 +1,398 @@ +.\" Man page generated from reStructuredText. +. +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.TH "BLUETOOTHCTL" 1 "November 2022" "BlueZ" "Linux System Administration" +.SH NAME +bluetoothctl \- Bluetooth Control Command Line Tool +.SH SYNOPSIS +.sp +\fBbluetoothctl\fP [\fB\-a\fP \fIcapability\fP] [\fB\-e\fP] [\fB\-m\fP] [\fB\-t\fP \fIseconds\fP] +[\fB\-v\fP] [\fB\-h\fP] +.SH DESCRIPTION +.sp +\fBbluetoothctl(1)\fP interactive bluetooth control tool. The tool works with +Bluetooth Classic (BR/EDR) and Bluetooth Low Energy (LE) controllers. +.sp +The tool is menu driven but can be automated from the command line. +Examples are given in the automation section. +.SH OPTIONS +.INDENT 0.0 +.TP +.BI \-a \ capability\fR,\fB \ \-\-agent \ capability +Register agent handler: +.TP +.B \-e\fP,\fB \-\-endpoints +Register Media endpoints +.TP +.B \-m\fP,\fB \-\-monitor +Enable monitor output +.TP +.BI \-t \ seconds\fR,\fB \ \-\-timeout \ seconds +Timeout in seconds for non\-interactive mode +.TP +.B \-v\fP,\fB \-\-version +Display version +.TP +.B \-h\fP,\fB \-\-help +Display help +.UNINDENT +.SH COMMANDS +.SS list +.sp +List available controllers. +.INDENT 0.0 +.TP +.B Usage +\fB# list\fP +.UNINDENT +.SS show +.sp +Controller information. +.INDENT 0.0 +.TP +.B Usage +\fB# show [ctrl]\fP +.UNINDENT +.SS select +.sp +Select default controller. +.INDENT 0.0 +.TP +.B Usage +\fB# select \fP +.UNINDENT +.SS devices +.sp +List available devices, with an optional property as the filter. +.INDENT 0.0 +.TP +.B Usage +\fB# devices [Paired/Bonded/Trusted/Connected]\fP +.UNINDENT +.SS system\-alias +.sp +Set controller alias. +.INDENT 0.0 +.TP +.B Usage +\fB# system\-alias \fP +.UNINDENT +.SS reset\-alias +.sp +Reset controller alias. +.INDENT 0.0 +.TP +.B Usage +\fB# reset\-alias\fP +.UNINDENT +.SS power +.sp +Set controller power. +.sp +When the controller is powered off, the USB port the controller is attached to +is put into a suspend state. +.INDENT 0.0 +.TP +.B Usage +\fB# power \fP +.UNINDENT +.SS advertise +.sp +Enable/disable advertising with given type. +.sp +If you exit the program advertising will be disabled. +.sp +When advertising the controller should advertise with random address but may +use its public address if it does not support the feature (address of the +device). +.sp +A device can advertise if it initiated the connection to another advertising +device. +.INDENT 0.0 +.TP +.B Usage +\fB# advertise \fP +.UNINDENT +.SS set\-alias +.sp +Set device alias. +.INDENT 0.0 +.TP +.B Usage +\fB# set\-alias \fP +.UNINDENT +.SS scan +.sp +Scan for devices. +.sp +For LE, scanning is an important requirement before connecting or pairing. +.sp +The purpose of scanning is to find devices that are advertising with their +discoverable flag set (either limited or general). Once you have found the +address then you can connect or pair. +.sp +Note the following when scanning: +.INDENT 0.0 +.INDENT 3.5 +.INDENT 0.0 +.IP \(bu 2 +When scanning the controller will use a random address that is not +resolvable so the public address is not leaked. A new random address is +created every time scan on is used. +.IP \(bu 2 +When turning on scanning the device will start receiving advertising reports +of what devices are advertising. +.IP \(bu 2 +The filtering of duplicate advertising reports may be enabled depending on +the filtering settings. +.IP \(bu 2 +Device objects found during a scan session will only be persisted if they +are connected/paired otherwise they are removed after some time. +.UNINDENT +.UNINDENT +.UNINDENT +.INDENT 0.0 +.TP +.B Usage +\fB# scan \fP +.UNINDENT +.SS pair +.sp +Pair with device. +.sp +This will pair with a device and then trust and connect to it. If the device is +already paired this will first remove the pairing. +.sp +The command can either be used while the controller is in the connected or not +connected state. +.sp +If the controller is already connected then the pair command can be used without +an arguments. If the controller is not connected, the pair command can be given +the address of a device with an active scan report and it will initiate the +connection before pairing. +.sp +Before pairing the agent must be selected to choose the authentication +mechanism. +.INDENT 0.0 +.TP +.B Usage +\fB# pair \fP +.UNINDENT +.SS pairable +.sp +Set controller pairable mode. +.sp +This enables/disables pairing. If pairing is disabled then the controller will +not accept any pairing requests. +.INDENT 0.0 +.TP +.B Usage +\fB# pairable \fP +.UNINDENT +.SS discoverable +.sp +Set discoverable mode. +.sp +This enables/disables discoverable mode. If discoverable is disabled then the +controller will not respond to any scan requests. +.sp +In LE if discoverable if off the controller will just passively scan and not +make scan requests to advertising devices. If on it will make the advertising +requests. +.sp +It will use a random address if supported by the controller. The length of time +\(dqdiscoverable on\(dq is valid is determined by discoverable\-timeout command. +.INDENT 0.0 +.TP +.B Usage +\fB# discoverable \fP +.UNINDENT +.SS discoverable\-timeout +.sp +Set discoverable timeout. +.sp +The time in seconds that \(dqdiscoverable on\(dq is valid. +.INDENT 0.0 +.TP +.B Usage +\fB# discoverable\-timeout [value]\fP +.UNINDENT +.SS agent +.sp +Enable/disable agent with given capability. +.sp +This chooses the local authentication mechanism of the controller. It is needed +for pairing and allows you to choose the IO capabilities of the controller. +.sp +The valid agent capabilities are: DisplayOnly, DisplayYesNo, KeyboardDisplay, +KeyboardOnly, NoInputNoOutput. +.INDENT 0.0 +.TP +.B Usage +\fB# agent \fP +.UNINDENT +.SS default\-agent +.sp +Set current agent as the default one. +.sp +After selecting the agent this will make it the default agent. +.INDENT 0.0 +.TP +.B Usage +\fB# default\-agent\fP +.UNINDENT +.SS trust +.sp +Trust device. +.INDENT 0.0 +.TP +.B Usage +\fB# trust \fP +.UNINDENT +.SS untrust +.sp +Untrust device. +.INDENT 0.0 +.TP +.B Usage +\fB# untrust \fP +.UNINDENT +.SS block +.sp +Block device. +.INDENT 0.0 +.TP +.B Usage +\fB# block \fP +.UNINDENT +.SS unblock +.sp +Unblock device +.INDENT 0.0 +.TP +.B Usage +\fB# unblock \fP +.UNINDENT +.SS remove +.sp +Remove device. +.INDENT 0.0 +.TP +.B Usage +\fB# remove \fP +.UNINDENT +.SS connect +.sp +Connect device. +.sp +This will initiate a connection to a device. +.sp +To connect with an LE device the controller must have an active scan report of +the device it wants to connect to. +.sp +If no advertising report is received before the timeout a +le\-connection\-abort\-by\-local error will be issued. In that case either try +again to connect assuming the device is advertising. +.INDENT 0.0 +.TP +.B Usage +\fB# connect \fP +.UNINDENT +.SS disconnect +.sp +Disconnect device. +.sp +For LE when disconnecting from an active connection the device address is not +needed. +.INDENT 0.0 +.TP +.B Usage +\fB# disconnect \fP +.UNINDENT +.SS info +.sp +Device information. +.INDENT 0.0 +.TP +.B Usage +\fB# info \fP +.UNINDENT +.SH ADVERTISE SUBMENU +.sp +See \fBbluetoothctl\-advertise(1)\fP\&. +.SH MONITOR SUBMENU +.sp +See \fBbluetoothctl\-monitor(1)\fP +.SH SCAN SUBMENU +.sp +See \fBbluetoothctl\-scan(1)\fP +.SH GATT SUBMENU +.sp +See \fBbluetoothctl\-gatt(1)\fP +.SH ADMIN SUBMENU +.sp +See \fBbluetoothctl\-admin(1)\fP +.SH PLAYER SUBMENU +.sp +See \fBbluetoothctl\-player(1)\fP +.SH ENDPOINT SUBMENU +.sp +See \fBbluetoothctl\-endpoint(1)\fP +.SH TRANSPORT SUBMENU +.sp +See \fBbluetoothctl\-transport(1)\fP +.SH MANAGEMENT SUBMENU +.sp +See \fBbluetoothctl\-mgmt(1)\fP +.SH AUTOMATION +.sp +Two common ways to automate the tool are to use Here Docs or the program expect. +Using Here Docs to show information about the Bluetooth controller. +.INDENT 0.0 +.INDENT 3.5 +.sp +.EX +bluetoothctl < +-e, --endpoints Register Media endpoints +-m, --monitor Enable monitor output +-t seconds, --timeout seconds Timeout in seconds for non-interactive mode +-v, --version Display version +-h, --help Display help + + +Commands +======== + +list +---- + +List available controllers. + +:Usage: **# list** + +show +---- + +Controller information. + +:Usage: **# show [ctrl]** + +select +------ + +Select default controller. + +:Usage: **# select ** + +devices +------- + +List available devices, with an optional property as the filter. + +:Usage: **# devices [Paired/Bonded/Trusted/Connected]** + +system-alias +------------ + +Set controller alias. + +:Usage: **# system-alias ** + +reset-alias +----------- + +Reset controller alias. + +:Usage: **# reset-alias** + +power +----- + +Set controller power. + +When the controller is powered off, the USB port the controller is attached to +is put into a suspend state. + +:Usage: **# power ** + +advertise +--------- + +Enable/disable advertising with given type. + +If you exit the program advertising will be disabled. + +When advertising the controller should advertise with random address but may +use its public address if it does not support the feature (address of the +device). + +A device can advertise if it initiated the connection to another advertising +device. + +:Usage: **# advertise ** + +set-alias +--------- + +Set device alias. + +:Usage: **# set-alias ** + +scan +---- + +Scan for devices. + +For LE, scanning is an important requirement before connecting or pairing. + +The purpose of scanning is to find devices that are advertising with their +discoverable flag set (either limited or general). Once you have found the +address then you can connect or pair. + +Note the following when scanning: + + - When scanning the controller will use a random address that is not + resolvable so the public address is not leaked. A new random address is + created every time scan on is used. + - When turning on scanning the device will start receiving advertising reports + of what devices are advertising. + - The filtering of duplicate advertising reports may be enabled depending on + the filtering settings. + - Device objects found during a scan session will only be persisted if they + are connected/paired otherwise they are removed after some time. + +:Usage: **# scan ** + +pair +---- + +Pair with device. + +This will pair with a device and then trust and connect to it. If the device is +already paired this will first remove the pairing. + +The command can either be used while the controller is in the connected or not +connected state. + +If the controller is already connected then the pair command can be used without +an arguments. If the controller is not connected, the pair command can be given +the address of a device with an active scan report and it will initiate the +connection before pairing. + +Before pairing the agent must be selected to choose the authentication +mechanism. + +:Usage: **# pair ** + +pairable +-------- + +Set controller pairable mode. + +This enables/disables pairing. If pairing is disabled then the controller will +not accept any pairing requests. + +:Usage: **# pairable ** + +discoverable +------------ + +Set discoverable mode. + +This enables/disables discoverable mode. If discoverable is disabled then the +controller will not respond to any scan requests. + +In LE if discoverable if off the controller will just passively scan and not +make scan requests to advertising devices. If on it will make the advertising +requests. + +It will use a random address if supported by the controller. The length of time +"discoverable on" is valid is determined by discoverable-timeout command. + +:Usage: **# discoverable ** + +discoverable-timeout +-------------------- + +Set discoverable timeout. + +The time in seconds that "discoverable on" is valid. + +:Usage: **# discoverable-timeout [value]** + +agent +----- + +Enable/disable agent with given capability. + +This chooses the local authentication mechanism of the controller. It is needed +for pairing and allows you to choose the IO capabilities of the controller. + +The valid agent capabilities are: DisplayOnly, DisplayYesNo, KeyboardDisplay, +KeyboardOnly, NoInputNoOutput. + +:Usage: **# agent ** + +default-agent +------------- + +Set current agent as the default one. + +After selecting the agent this will make it the default agent. + +:Usage: **# default-agent** + +trust +----- + +Trust device. + +:Usage: **# trust ** + +untrust +------- + +Untrust device. + +:Usage: **# untrust ** + +block +----- + +Block device. + +:Usage: **# block ** + +unblock +------- +Unblock device + +:Usage: **# unblock ** + +remove +------ + +Remove device. + +:Usage: **# remove ** + +connect +------- + +Connect device. + +This will initiate a connection to a device. + +To connect with an LE device the controller must have an active scan report of +the device it wants to connect to. + +If no advertising report is received before the timeout a +le-connection-abort-by-local error will be issued. In that case either try +again to connect assuming the device is advertising. + +:Usage: **# connect ** + +disconnect +---------- + +Disconnect device. + +For LE when disconnecting from an active connection the device address is not +needed. + +:Usage: **# disconnect ** + +info +---- + +Device information. + +:Usage: **# info ** + + +Advertise Submenu +================= + +See **bluetoothctl-advertise(1)**. + +Monitor Submenu +=============== + +See **bluetoothctl-monitor(1)** + +Scan Submenu +============ + +See **bluetoothctl-scan(1)** + +Gatt Submenu +============ + +See **bluetoothctl-gatt(1)** + +Admin Submenu +============= + +See **bluetoothctl-admin(1)** + +Player Submenu +============== + +See **bluetoothctl-player(1)** + +Endpoint Submenu +================ + +See **bluetoothctl-endpoint(1)** + +Transport Submenu +================= + +See **bluetoothctl-transport(1)** + +Management Submenu +================== + +See **bluetoothctl-mgmt(1)** + +AUTOMATION +========== +Two common ways to automate the tool are to use Here Docs or the program expect. +Using Here Docs to show information about the Bluetooth controller. + +.. code:: bash + + bluetoothctl <tv_sec) + NSEC_USEC((_ts)->tv_nsec)) -#define EP_SRC_LOCATIONS 0x00000001 +#define EP_SRC_LOCATIONS 0x00000003 #define EP_SNK_LOCATIONS 0x00000003 #define EP_SRC_CTXT 0x000f @@ -1860,7 +1860,7 @@ struct iovec *caps; struct iovec *meta; uint8_t target_latency; - const struct codec_qos *qos; + struct codec_qos qos; }; #define BCODE {0x01, 0x02, 0x68, 0x05, 0x53, 0xf1, 0x41, 0x5a, \ @@ -1886,7 +1886,7 @@ static void append_io_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { - struct codec_qos *qos = (void *)cfg->qos; + struct codec_qos *qos = &cfg->qos; bt_shell_printf("Interval %u\n", qos->interval); @@ -1897,7 +1897,7 @@ g_dbus_dict_append_entry(iter, "PHY", DBUS_TYPE_BYTE, &qos->phy); - bt_shell_printf("SDU %u\n", cfg->qos->sdu); + bt_shell_printf("SDU %u\n", qos->sdu); g_dbus_dict_append_entry(iter, "SDU", DBUS_TYPE_UINT16, &qos->sdu); @@ -1914,7 +1914,7 @@ static void append_ucast_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { - struct codec_qos *qos = (void *)cfg->qos; + struct codec_qos *qos = &cfg->qos; if (cfg->ep->iso_group != BT_ISO_QOS_GROUP_UNSET) { bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->iso_group); @@ -2020,7 +2020,7 @@ static void append_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { DBusMessageIter entry, var, dict; - struct codec_qos *qos = (void *)cfg->qos; + struct codec_qos *qos = &cfg->qos; const char *key = "QoS"; if (!qos) @@ -2104,13 +2104,46 @@ return *iov; } +static int parse_chan_alloc(DBusMessageIter *iter, uint32_t *location, + uint8_t *channels) +{ + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + int var; + + dbus_message_iter_recurse(iter, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + var = dbus_message_iter_get_arg_type(&value); + + if (!strcasecmp(key, "ChannelAllocation")) { + if (var != DBUS_TYPE_UINT32) + return -EINVAL; + dbus_message_iter_get_basic(&value, location); + if (*channels) + *channels = __builtin_popcount(*location); + return 0; + } + + dbus_message_iter_next(iter); + } + + return -EINVAL; +} + static DBusMessage *endpoint_select_properties_reply(struct endpoint *ep, DBusMessage *msg, struct codec_preset *preset) { DBusMessage *reply; - DBusMessageIter iter; + DBusMessageIter iter, props; struct endpoint_config *cfg; + uint32_t location = 0; + uint8_t channels = 1; if (!preset) return NULL; @@ -2126,13 +2159,31 @@ iov_append(&cfg->caps, preset->data.iov_base, preset->data.iov_len); cfg->target_latency = preset->target_latency; + dbus_message_iter_init(msg, &iter); + dbus_message_iter_recurse(&iter, &props); + + if (!parse_chan_alloc(&props, &location, &channels)) { + uint8_t chan_alloc_ltv[] = { + 0x05, LC3_CONFIG_CHAN_ALLOC, location & 0xff, + location >> 8, location >> 16, location >> 24 + }; + + iov_append(&cfg->caps, &chan_alloc_ltv, sizeof(chan_alloc_ltv)); + } + /* Copy metadata */ if (ep->meta) iov_append(&cfg->meta, ep->meta->iov_base, ep->meta->iov_len); - if (preset->qos.phy) + if (preset->qos.phy) { /* Set QoS parameters */ - cfg->qos = &preset->qos; + cfg->qos = preset->qos; + /* Adjust the SDU size based on the number of + * locations/channels that is being requested. + */ + if (channels > 1) + cfg->qos.sdu *= channels; + } dbus_message_iter_init_append(reply, &iter); @@ -3136,7 +3187,7 @@ } /* Set QoS parameters */ - cfg->qos = &preset->qos; + cfg->qos = preset->qos; endpoint_set_config(cfg); return; diff -Nru bluez-5.71/configure bluez-5.72/configure --- bluez-5.71/configure 2023-12-14 05:41:59.000000000 +0800 +++ bluez-5.72/configure 2024-01-13 06:27:06.000000000 +0800 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for bluez 5.71. +# Generated by GNU Autoconf 2.71 for bluez 5.72. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, @@ -618,8 +618,8 @@ # Identity of this package. PACKAGE_NAME='bluez' PACKAGE_TARNAME='bluez' -PACKAGE_VERSION='5.71' -PACKAGE_STRING='bluez 5.71' +PACKAGE_VERSION='5.72' +PACKAGE_STRING='bluez 5.72' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1569,7 +1569,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bluez 5.71 to adapt to many kinds of systems. +\`configure' configures bluez 5.72 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1640,7 +1640,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bluez 5.71:";; + short | recursive ) echo "Configuration of bluez 5.72:";; esac cat <<\_ACEOF @@ -1849,7 +1849,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bluez configure 5.71 +bluez configure 5.72 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2067,7 +2067,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bluez $as_me 5.71, which was +It was created by bluez $as_me 5.72, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3337,7 +3337,7 @@ # Define the identity of the package. PACKAGE='bluez' - VERSION='5.71' + VERSION='5.72' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -17374,7 +17374,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bluez $as_me 5.71, which was +This file was extended by bluez $as_me 5.72, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -17442,7 +17442,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -bluez config.status 5.71 +bluez config.status 5.72 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff -Nru bluez-5.71/configure.ac bluez-5.72/configure.ac --- bluez-5.71/configure.ac 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/configure.ac 2024-01-13 06:25:53.000000000 +0800 @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 AC_PREREQ(2.60) -AC_INIT(bluez, 5.71) +AC_INIT(bluez, 5.72) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules tar-pax no-dist-gzip dist-xz]) diff -Nru bluez-5.71/debian/bluez.install bluez-5.72/debian/bluez.install --- bluez-5.71/debian/bluez.install 2024-01-10 23:44:40.000000000 +0800 +++ bluez-5.72/debian/bluez.install 2024-01-16 15:15:23.000000000 +0800 @@ -29,3 +29,4 @@ usr/share/dbus-1/system-services/org.bluez.service usr/share/zsh/site-functions/_bluetoothctl debian/source_bluez.py usr/share/apport/package-hooks +usr/share/man/man1/bluetoothctl.1 diff -Nru bluez-5.71/debian/changelog bluez-5.72/debian/changelog --- bluez-5.71/debian/changelog 2024-01-13 01:16:54.000000000 +0800 +++ bluez-5.72/debian/changelog 2024-01-16 15:15:23.000000000 +0800 @@ -1,3 +1,16 @@ +bluez (5.72-0ubuntu1) noble; urgency=medium + + * New upstream release 5.72 (LP: #2049352): + - Fix issue with BAP and handling stream IO linking. + - Fix issue with BAP and setup of multiple streams per endpoint. + - Fix issue with AVDTP and potential incorrect transaction label. + - Fix issue with A2DP and handling crash on suspend. + - Fix issue with GATT database and an invalid pointer. + - Add support for AICS service. + * Add bluetoothctl.1 man page to bluez package. + + -- Daniel van Vugt Tue, 16 Jan 2024 15:15:23 +0800 + bluez (5.71-1ubuntu3) noble; urgency=medium * Merge from Debian unstable. Remaining changes: diff -Nru bluez-5.71/doc/org.bluez.MediaEndpoint.5 bluez-5.72/doc/org.bluez.MediaEndpoint.5 --- bluez-5.71/doc/org.bluez.MediaEndpoint.5 2023-12-14 05:57:47.000000000 +0800 +++ bluez-5.72/doc/org.bluez.MediaEndpoint.5 2024-01-13 06:42:41.000000000 +0800 @@ -101,6 +101,8 @@ .TP .B uint32 Locations .TP +.B uint32_t ChannelAllocation +.TP .B dict QoS .INDENT 7.0 .TP diff -Nru bluez-5.71/doc/org.bluez.MediaEndpoint.rst bluez-5.72/doc/org.bluez.MediaEndpoint.rst --- bluez-5.71/doc/org.bluez.MediaEndpoint.rst 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/doc/org.bluez.MediaEndpoint.rst 2024-01-13 06:25:53.000000000 +0800 @@ -79,6 +79,8 @@ :uint32 Locations: + :uint32_t ChannelAllocation: + :dict QoS: :byte Framing: diff -Nru bluez-5.71/lib/uuid.h bluez-5.72/lib/uuid.h --- bluez-5.71/lib/uuid.h 2023-09-29 03:53:28.000000000 +0800 +++ bluez-5.72/lib/uuid.h 2024-01-13 06:25:53.000000000 +0800 @@ -187,6 +187,13 @@ #define VOCS_CP_CHRC_UUID 0x2B82 #define VOCS_AUDIO_OP_DESC_CHAR_UUID 0x2B83 +#define AICS_INPUT_STATE_CHAR_UUID 0x2B77 +#define AICS_GAIN_SETTING_PROP_CHAR_UUID 0x2B78 +#define AICS_AUDIO_INPUT_TYPE_CHAR_UUID 0x2B79 +#define AICS_INPUT_STATUS_CHAR_UUID 0X2B7A +#define AICS_AUDIO_INPUT_CP_CHRC_UUID 0X2B7B +#define AICS_INPUT_DESCR_CHAR_UUID 0X2B7C + #define GMCS_UUID 0x1849 #define MEDIA_PLAYER_NAME_CHRC_UUID 0x2b93 #define MEDIA_TRACK_CHNGD_CHRC_UUID 0x2b96 diff -Nru bluez-5.71/Makefile.am bluez-5.72/Makefile.am --- bluez-5.71/Makefile.am 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/Makefile.am 2024-01-13 06:25:53.000000000 +0800 @@ -82,7 +82,7 @@ lib_LTLIBRARIES += lib/libbluetooth.la lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) -lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 22:11:19 +lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 22:12:19 lib_libbluetooth_la_DEPENDENCIES = $(local_headers) endif diff -Nru bluez-5.71/Makefile.in bluez-5.72/Makefile.in --- bluez-5.71/Makefile.in 2023-12-14 05:41:55.000000000 +0800 +++ bluez-5.72/Makefile.in 2024-01-13 06:27:03.000000000 +0800 @@ -213,7 +213,8 @@ @TOOLS_TRUE@ tools/gatt-service profiles/iap/iapd @MANPAGES_TRUE@@TOOLS_TRUE@am__append_51 = tools/rctest.1 tools/l2ping.1 tools/btattach.1 tools/isotest.1 \ -@MANPAGES_TRUE@@TOOLS_TRUE@ tools/btmgmt.1 client/bluetoothctl-mgmt.1 \ +@MANPAGES_TRUE@@TOOLS_TRUE@ tools/btmgmt.1 client/bluetoothctl.1 \ +@MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-mgmt.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-monitor.1 client/bluetoothctl-admin.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-advertise.1 client/bluetoothctl-endpoint.1 \ @MANPAGES_TRUE@@TOOLS_TRUE@ client/bluetoothctl-gatt.1 client/bluetoothctl-player.1 \ @@ -3494,12 +3495,13 @@ tools/hciconfig.1 tools/hcitool.1 tools/hcidump.1 \ tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1 tools/rctest.1 \ tools/l2ping.1 tools/btattach.1 tools/bdaddr.1 tools/isotest.1 \ - tools/btmgmt.1 client/bluetoothctl-mgmt.1 \ - client/bluetoothctl-monitor.1 client/bluetoothctl-admin.1 \ - client/bluetoothctl-advertise.1 client/bluetoothctl-endpoint.1 \ - client/bluetoothctl-gatt.1 client/bluetoothctl-player.1 \ - client/bluetoothctl-scan.1 client/bluetoothctl-transport.1 \ - tools/hid2hci.1 $(am__append_74) + tools/btmgmt.1 client/bluetoothctl.1 \ + client/bluetoothctl-mgmt.1 client/bluetoothctl-monitor.1 \ + client/bluetoothctl-admin.1 client/bluetoothctl-advertise.1 \ + client/bluetoothctl-endpoint.1 client/bluetoothctl-gatt.1 \ + client/bluetoothctl-player.1 client/bluetoothctl-scan.1 \ + client/bluetoothctl-transport.1 tools/hid2hci.1 \ + $(am__append_74) plugin_LTLIBRARIES = $(am__append_29) $(am__append_42) \ $(am__append_67) lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c @@ -3513,7 +3515,7 @@ BUILT_SOURCES = $(local_headers) $(ell_built_sources) src/builtin.h \ obexd/src/builtin.h @LIBRARY_TRUE@lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) -@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 22:11:19 +@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 22:12:19 @LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers) lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \ $(extra_headers) $(extra_sources) diff -Nru bluez-5.71/Makefile.tools bluez-5.72/Makefile.tools --- bluez-5.71/Makefile.tools 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/Makefile.tools 2024-01-13 06:25:53.000000000 +0800 @@ -348,7 +348,8 @@ if MANPAGES man_MANS += tools/rctest.1 tools/l2ping.1 tools/btattach.1 tools/isotest.1 \ - tools/btmgmt.1 client/bluetoothctl-mgmt.1 \ + tools/btmgmt.1 client/bluetoothctl.1 \ + client/bluetoothctl-mgmt.1 \ client/bluetoothctl-monitor.1 client/bluetoothctl-admin.1 \ client/bluetoothctl-advertise.1 client/bluetoothctl-endpoint.1 \ client/bluetoothctl-gatt.1 client/bluetoothctl-player.1 \ @@ -475,6 +476,7 @@ tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1 \ tools/rctest.1 tools/l2ping.1 tools/btattach.1 \ tools/bdaddr.1 tools/isotest.1 tools/btmgmt.1 \ + client/bluetoothctl.1 \ client/bluetoothctl-mgmt.1 \ client/bluetoothctl-monitor.1 \ client/bluetoothctl-admin.1 \ diff -Nru bluez-5.71/monitor/packet.c bluez-5.72/monitor/packet.c --- bluez-5.71/monitor/packet.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/monitor/packet.c 2024-01-13 06:25:53.000000000 +0800 @@ -4286,10 +4286,26 @@ */ index_list[index].msft_opcode = 0xFC1E; break; + case 29: + /* + * Qualcomm controllers that support the + * Microsoft vendor extensions are using + * 0xFD70 for VsMsftOpCode. + */ + index_list[index].msft_opcode = 0xFD70; + break; + case 70: + /* + * Mediatek controllers that support the + * Microsoft vendor extensions are using + * 0xFD30 for VsMsftOpCode. + */ + index_list[index].msft_opcode = 0xFD30; + break; case 93: /* * Realtek controllers that support the - * Microsoft vendor extenions are using + * Microsoft vendor extensions are using * 0xFCF0 for VsMsftOpCode. */ index_list[index].msft_opcode = 0xFCF0; @@ -4298,7 +4314,7 @@ /* * Emulator controllers use Linux Foundation as * manufacturer and support the - * Microsoft vendor extenions using + * Microsoft vendor extensions using * 0xFC1E for VsMsftOpCode. */ index_list[index].msft_opcode = 0xFC1E; diff -Nru bluez-5.71/profiles/audio/avdtp.c bluez-5.72/profiles/audio/avdtp.c --- bluez-5.71/profiles/audio/avdtp.c 2022-07-25 05:02:15.000000000 +0800 +++ bluez-5.72/profiles/audio/avdtp.c 2024-01-13 06:25:53.000000000 +0800 @@ -286,7 +286,6 @@ gboolean active; int no_of_packets; uint8_t transaction; - uint8_t message_type; uint8_t signal_id; uint8_t buf[1024]; uint8_t data_size; @@ -397,7 +396,8 @@ uint16_t imtu; uint16_t omtu; - struct in_buf in; + struct in_buf in_resp; + struct in_buf in_cmd; char *buf; @@ -1462,15 +1462,16 @@ if (err != NULL) { rej.error = AVDTP_UNSUPPORTED_CONFIGURATION; rej.category = err->err.error_code; - avdtp_send(session, session->in.transaction, - AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, - &rej, sizeof(rej)); + avdtp_send(session, session->in_cmd.transaction, + AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, + &rej, sizeof(rej)); stream_free(stream); return; } - if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_SET_CONFIGURATION, NULL, 0)) { + if (!avdtp_send(session, session->in_cmd.transaction, + AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SET_CONFIGURATION, NULL, 0)) { stream_free(stream); return; } @@ -2092,6 +2093,12 @@ struct avdtp_start_header *start = (void *) session->buf; void *payload; gsize payload_size; + struct in_buf *in; + + if (header->message_type == AVDTP_MSG_TYPE_COMMAND) + in = &session->in_cmd; + else + in = &session->in_resp; switch (header->packet_type) { case AVDTP_PKT_TYPE_SINGLE: @@ -2099,7 +2106,7 @@ error("Received too small single packet (%zu bytes)", size); return PARSE_ERROR; } - if (session->in.active) { + if (in->active) { error("SINGLE: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } @@ -2107,12 +2114,11 @@ payload = session->buf + sizeof(*single); payload_size = size - sizeof(*single); - session->in.active = TRUE; - session->in.data_size = 0; - session->in.no_of_packets = 1; - session->in.transaction = header->transaction; - session->in.message_type = header->message_type; - session->in.signal_id = single->signal_id; + in->active = TRUE; + in->data_size = 0; + in->no_of_packets = 1; + in->transaction = header->transaction; + in->signal_id = single->signal_id; break; case AVDTP_PKT_TYPE_START: @@ -2120,17 +2126,16 @@ error("Received too small start packet (%zu bytes)", size); return PARSE_ERROR; } - if (session->in.active) { + if (in->active) { error("START: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } - session->in.active = TRUE; - session->in.data_size = 0; - session->in.transaction = header->transaction; - session->in.message_type = header->message_type; - session->in.no_of_packets = start->no_of_packets; - session->in.signal_id = start->signal_id; + in->active = TRUE; + in->data_size = 0; + in->transaction = header->transaction; + in->no_of_packets = start->no_of_packets; + in->signal_id = start->signal_id; payload = session->buf + sizeof(*start); payload_size = size - sizeof(*start); @@ -2142,15 +2147,15 @@ size); return PARSE_ERROR; } - if (!session->in.active) { + if (!in->active) { error("CONTINUE: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } - if (session->in.transaction != header->transaction) { + if (in->transaction != header->transaction) { error("Continue transaction id doesn't match"); return PARSE_ERROR; } - if (session->in.no_of_packets <= 1) { + if (in->no_of_packets <= 1) { error("Too few continue packets"); return PARSE_ERROR; } @@ -2164,15 +2169,15 @@ error("Received too small end packet (%zu bytes)", size); return PARSE_ERROR; } - if (!session->in.active) { + if (!in->active) { error("END: Invalid AVDTP packet fragmentation"); return PARSE_ERROR; } - if (session->in.transaction != header->transaction) { + if (in->transaction != header->transaction) { error("End transaction id doesn't match"); return PARSE_ERROR; } - if (session->in.no_of_packets > 1) { + if (in->no_of_packets > 1) { error("Got an end packet too early"); return PARSE_ERROR; } @@ -2186,23 +2191,23 @@ return PARSE_ERROR; } - if (session->in.data_size + payload_size > - sizeof(session->in.buf)) { + if (in->data_size + payload_size > + sizeof(in->buf)) { error("Not enough incoming buffer space!"); return PARSE_ERROR; } - memcpy(session->in.buf + session->in.data_size, payload, payload_size); - session->in.data_size += payload_size; + memcpy(in->buf + in->data_size, payload, payload_size); + in->data_size += payload_size; - if (session->in.no_of_packets > 1) { - session->in.no_of_packets--; + if (in->no_of_packets > 1) { + in->no_of_packets--; DBG("Received AVDTP fragment. %d to go", - session->in.no_of_packets); + in->no_of_packets); return PARSE_FRAGMENT; } - session->in.active = FALSE; + in->active = FALSE; return PARSE_SUCCESS; } @@ -2246,11 +2251,11 @@ break; } - if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) { - if (!avdtp_parse_cmd(session, session->in.transaction, - session->in.signal_id, - session->in.buf, - session->in.data_size)) { + if (header->message_type == AVDTP_MSG_TYPE_COMMAND) { + if (!avdtp_parse_cmd(session, session->in_cmd.transaction, + session->in_cmd.signal_id, + session->in_cmd.buf, + session->in_cmd.data_size)) { error("Unable to handle command. Disconnecting"); goto failed; } @@ -2273,7 +2278,7 @@ return TRUE; } - if (session->in.signal_id != session->req->signal_id) { + if (session->in_resp.signal_id != session->req->signal_id) { error("Response signal doesn't match"); return TRUE; } @@ -2284,20 +2289,20 @@ switch (header->message_type) { case AVDTP_MSG_TYPE_ACCEPT: if (!avdtp_parse_resp(session, session->req->stream, - session->in.transaction, - session->in.signal_id, - session->in.buf, - session->in.data_size)) { + session->in_resp.transaction, + session->in_resp.signal_id, + session->in_resp.buf, + session->in_resp.data_size)) { error("Unable to parse accept response"); goto failed; } break; case AVDTP_MSG_TYPE_REJECT: if (!avdtp_parse_rej(session, session->req->stream, - session->in.transaction, - session->in.signal_id, - session->in.buf, - session->in.data_size)) { + session->in_resp.transaction, + session->in_resp.signal_id, + session->in_resp.buf, + session->in_resp.data_size)) { error("Unable to parse reject response"); goto failed; } diff -Nru bluez-5.71/profiles/audio/bap.c bluez-5.72/profiles/audio/bap.c --- bluez-5.71/profiles/audio/bap.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/profiles/audio/bap.c 2024-01-13 06:25:53.000000000 +0800 @@ -62,22 +62,27 @@ #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" #define MEDIA_INTERFACE "org.bluez.Media1" -struct bap_ep { - char *path; - struct bap_data *data; - struct bt_bap_pac *lpac; - struct bt_bap_pac *rpac; +struct bap_setup { + struct bap_ep *ep; struct bt_bap_stream *stream; + struct bt_bap_qos qos; GIOChannel *io; unsigned int io_id; bool recreate; bool cig_active; struct iovec *caps; struct iovec *metadata; - struct bt_bap_qos qos; unsigned int id; - DBusMessage *msg; struct iovec *base; + DBusMessage *msg; +}; + +struct bap_ep { + char *path; + struct bap_data *data; + struct bt_bap_pac *lpac; + struct bt_bap_pac *rpac; + struct queue *setups; }; struct bap_data { @@ -728,84 +733,131 @@ static void qos_cb(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { - struct bap_ep *ep = user_data; + struct bap_setup *setup = user_data; DBusMessage *reply; DBG("stream %p code 0x%02x reason 0x%02x", stream, code, reason); - ep->id = 0; + setup->id = 0; - if (!ep->msg) + if (!setup->msg) return; if (!code) - reply = dbus_message_new_method_return(ep->msg); + reply = dbus_message_new_method_return(setup->msg); else - reply = btd_error_failed(ep->msg, "Unable to configure"); + reply = btd_error_failed(setup->msg, "Unable to configure"); g_dbus_send_message(btd_get_dbus_connection(), reply); - dbus_message_unref(ep->msg); - ep->msg = NULL; + dbus_message_unref(setup->msg); + setup->msg = NULL; } static void config_cb(struct bt_bap_stream *stream, uint8_t code, uint8_t reason, void *user_data) { - struct bap_ep *ep = user_data; + struct bap_setup *setup = user_data; DBusMessage *reply; DBG("stream %p code 0x%02x reason 0x%02x", stream, code, reason); - ep->id = 0; + setup->id = 0; if (!code) return; - if (!ep->msg) + if (!setup->msg) return; - reply = btd_error_failed(ep->msg, "Unable to configure"); + reply = btd_error_failed(setup->msg, "Unable to configure"); g_dbus_send_message(btd_get_dbus_connection(), reply); - dbus_message_unref(ep->msg); - ep->msg = NULL; + dbus_message_unref(setup->msg); + setup->msg = NULL; } -static void bap_io_close(struct bap_ep *ep) +static void setup_io_close(void *data, void *user_data) { + struct bap_setup *setup = data; int fd; - if (ep->io_id) { - g_source_remove(ep->io_id); - ep->io_id = 0; + if (setup->io_id) { + g_source_remove(setup->io_id); + setup->io_id = 0; } - if (!ep->io) + if (!setup->io) return; - DBG("ep %p", ep); + DBG("setup %p", setup); - fd = g_io_channel_unix_get_fd(ep->io); + fd = g_io_channel_unix_get_fd(setup->io); close(fd); - g_io_channel_unref(ep->io); - ep->io = NULL; - ep->cig_active = false; + g_io_channel_unref(setup->io); + setup->io = NULL; + setup->cig_active = false; + + bt_bap_stream_io_connecting(setup->stream, -1); +} + +static void ep_close(struct bap_ep *ep) +{ + if (!ep) + return; + + queue_foreach(ep->setups, setup_io_close, NULL); +} + +static struct bap_setup *setup_new(struct bap_ep *ep) +{ + struct bap_setup *setup; + + setup = new0(struct bap_setup, 1); + setup->ep = ep; + + if (!ep->setups) + ep->setups = queue_new(); + + queue_push_tail(ep->setups, setup); + + DBG("ep %p setup %p", ep, setup); + + return setup; +} + +static void setup_free(void *data) +{ + struct bap_setup *setup = data; + + DBG("%p", setup); + + if (setup->ep) + queue_remove(setup->ep->setups, setup); + + setup_io_close(setup, NULL); + + util_iov_free(setup->caps, 1); + util_iov_free(setup->metadata, 1); + util_iov_free(setup->base, 1); + + if (bt_bap_stream_get_type(setup->stream) == BT_BAP_STREAM_TYPE_BCAST) + util_iov_free(setup->qos.bcast.bcode, 1); + + free(setup); } static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg, void *data) { struct bap_ep *ep = data; + struct bap_setup *setup; const char *path; DBusMessageIter args, props; - if (ep->msg) - return btd_error_busy(msg); - dbus_message_iter_init(msg, &args); dbus_message_iter_get_basic(&args, &path); @@ -815,59 +867,55 @@ if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) return btd_error_invalid_args(msg); - /* Disconnect IO if connecting since QoS is going to be reconfigured */ - if (bt_bap_stream_io_is_connecting(ep->stream, NULL)) { - bap_io_close(ep); - bt_bap_stream_io_connecting(ep->stream, -1); - } + /* Disconnect IOs if connecting since QoS is going to be reconfigured */ + ep_close(ep); + + setup = setup_new(ep); if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) { /* Mark BIG and BIS to be auto assigned */ - ep->qos.bcast.big = BT_ISO_QOS_BIG_UNSET; - ep->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET; + setup->qos.bcast.big = BT_ISO_QOS_BIG_UNSET; + setup->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET; } else { /* Mark CIG and CIS to be auto assigned */ - ep->qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET; - ep->qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET; + setup->qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET; + setup->qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET; } - if (parse_configuration(&props, &ep->caps, &ep->metadata, - &ep->base, &ep->qos) < 0) { + if (parse_configuration(&props, &setup->caps, &setup->metadata, + &setup->base, &setup->qos) < 0) { DBG("Unable to parse configuration"); + setup_free(setup); return btd_error_invalid_args(msg); } - /* TODO: Check if stream capabilities match add support for Latency - * and PHY. - */ - if (!ep->stream) - ep->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, - ep->rpac, &ep->qos, ep->caps); - - ep->id = bt_bap_stream_config(ep->stream, &ep->qos, ep->caps, - config_cb, ep); - if (!ep->id) { + setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep->rpac, + &setup->qos, setup->caps); + + setup->id = bt_bap_stream_config(setup->stream, &setup->qos, + setup->caps, config_cb, ep); + if (!setup->id) { DBG("Unable to config stream"); - free(ep->caps); - ep->caps = NULL; + setup_free(setup); return btd_error_invalid_args(msg); } - bt_bap_stream_set_user_data(ep->stream, ep->path); + bt_bap_stream_set_user_data(setup->stream, ep->path); - if (ep->metadata && ep->metadata->iov_len) - bt_bap_stream_metadata(ep->stream, ep->metadata, NULL, NULL); + if (setup->metadata && setup->metadata->iov_len) + bt_bap_stream_metadata(setup->stream, setup->metadata, NULL, + NULL); - switch (bt_bap_stream_get_type(ep->stream)) { + switch (bt_bap_stream_get_type(setup->stream)) { case BT_BAP_STREAM_TYPE_UCAST: - ep->msg = dbus_message_ref(msg); + setup->msg = dbus_message_ref(msg); break; case BT_BAP_STREAM_TYPE_BCAST: /* No message sent over the air for broadcast */ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) - ep->msg = dbus_message_ref(msg); + setup->msg = dbus_message_ref(msg); else - ep->id = 0; + setup->id = 0; return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -901,20 +949,14 @@ sizeof(qos->bcast.bcode)); } -static bool match_ep_type(const void *data, const void *user_data) -{ - const struct bap_ep *ep = data; - - return (bt_bap_pac_get_type(ep->lpac) == PTR_TO_INT(user_data)); -} - static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data) { - struct bap_data *data = user_data; + struct bap_setup *setup = user_data; + struct bap_ep *ep = setup->ep; + struct bap_data *data = ep->data; struct bt_iso_qos qos; struct bt_iso_base base; char address[18]; - struct bap_ep *ep; int fd; struct iovec *base_io; uint32_t presDelay; @@ -938,32 +980,28 @@ DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)", address, qos.bcast.big, qos.bcast.bis); - ep = queue_find(data->bcast, match_ep_type, - INT_TO_PTR(BT_BAP_BCAST_SINK)); - if (!ep) - return; - - update_bcast_qos(&qos, &ep->qos); + update_bcast_qos(&qos, &setup->qos); base_io = new0(struct iovec, 1); util_iov_memcpy(base_io, base.base, base.base_len); parse_base(base_io->iov_base, base_io->iov_len, bap_debug, &presDelay, &numSubgroups, &numBis, - &codec, &ep->caps, &ep->metadata); + &codec, &setup->caps, &setup->metadata); /* Update pac with BASE information */ - bt_bap_update_bcast_source(ep->rpac, &codec, ep->caps, ep->metadata); - ep->id = bt_bap_stream_config(ep->stream, &ep->qos, - ep->caps, NULL, NULL); + bt_bap_update_bcast_source(ep->rpac, &codec, setup->caps, + setup->metadata); + setup->id = bt_bap_stream_config(setup->stream, &setup->qos, + setup->caps, NULL, NULL); data->listen_io = io; - bt_bap_stream_set_user_data(ep->stream, ep->path); + bt_bap_stream_set_user_data(setup->stream, ep->path); fd = g_io_channel_unix_get_fd(io); - if (bt_bap_stream_set_io(ep->stream, fd)) { - bt_bap_stream_enable(ep->stream, true, NULL, NULL, NULL); + if (bt_bap_stream_set_io(setup->stream, fd)) { + bt_bap_stream_enable(setup->stream, true, NULL, NULL, NULL); g_io_channel_set_close_on_unref(io, FALSE); return; } @@ -1008,16 +1046,10 @@ static void ep_free(void *data) { struct bap_ep *ep = data; + struct queue *setups = ep->setups; - if (ep->id) - bt_bap_stream_cancel(ep->stream, ep->id); - - bap_io_close(ep); - - util_iov_free(ep->caps, 1); - util_iov_free(ep->metadata, 1); - if (bt_bap_stream_get_type(ep->stream) == BT_BAP_STREAM_TYPE_BCAST) - util_iov_free(ep->qos.bcast.bcode, 1); + ep->setups = NULL; + queue_destroy(setups, setup_free); free(ep->path); free(ep); } @@ -1077,12 +1109,10 @@ case BT_BAP_BCAST_SOURCE: err = asprintf(&ep->path, "%s/pac_%s%d", adapter_get_path(adapter), suffix, i); - ep->base = new0(struct iovec, 1); break; case BT_BAP_BCAST_SINK: err = asprintf(&ep->path, "%s/pac_%s%d", device_get_path(device), suffix, i); - ep->base = new0(struct iovec, 1); break; } @@ -1181,33 +1211,38 @@ return ep; } -static void bap_config(void *data, void *user_data) +static void setup_config(void *data, void *user_data) { - struct bap_ep *ep = data; + struct bap_setup *setup = data; + struct bap_ep *ep = setup->ep; - DBG("ep %p caps %p metadata %p", ep, ep->caps, ep->metadata); - - if (!ep->caps) - return; + DBG("setup %p caps %p metadata %p", setup, setup->caps, + setup->metadata); /* TODO: Check if stream capabilities match add support for Latency * and PHY. */ - if (!ep->stream) - ep->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, - ep->rpac, &ep->qos, ep->caps); - - ep->id = bt_bap_stream_config(ep->stream, &ep->qos, ep->caps, - config_cb, ep); - if (!ep->id) { + if (!setup->stream) + setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, + ep->rpac, &setup->qos, + setup->caps); + + setup->id = bt_bap_stream_config(setup->stream, &setup->qos, + setup->caps, config_cb, setup); + if (!setup->id) { DBG("Unable to config stream"); - util_iov_free(ep->caps, 1); - ep->caps = NULL; - util_iov_free(ep->metadata, 1); - ep->metadata = NULL; + setup_free(setup); + return; } - bt_bap_stream_set_user_data(ep->stream, ep->path); + bt_bap_stream_set_user_data(setup->stream, ep->path); +} + +static void bap_config(void *data, void *user_data) +{ + struct bap_ep *ep = data; + + queue_foreach(ep->setups, setup_config, NULL); } static void select_cb(struct bt_bap_pac *pac, int err, struct iovec *caps, @@ -1215,6 +1250,7 @@ void *user_data) { struct bap_ep *ep = user_data; + struct bap_setup *setup; if (err) { error("err %d", err); @@ -1222,15 +1258,10 @@ goto done; } - util_iov_free(ep->caps, 1); - ep->caps = util_iov_dup(caps, 1); - - if (metadata && metadata->iov_base && metadata->iov_len) { - ep->metadata = util_iov_dup(metadata, 1); - bt_bap_stream_metadata(ep->stream, ep->metadata, NULL, NULL); - } - - ep->qos = *qos; + setup = setup_new(ep); + setup->caps = util_iov_dup(caps, 1); + setup->metadata = util_iov_dup(metadata, 1); + setup->qos = *qos; DBG("selecting %d", ep->data->selecting); ep->data->selecting--; @@ -1259,10 +1290,8 @@ } /* TODO: Cache LRU? */ - if (btd_service_is_initiator(service)) { - if (!bt_bap_select(lpac, rpac, select_cb, ep)) - ep->data->selecting++; - } + if (btd_service_is_initiator(service)) + bt_bap_select(lpac, rpac, &ep->data->selecting, select_cb, ep); return true; } @@ -1293,30 +1322,42 @@ bt_bap_foreach_pac(bap, BT_BAP_SINK, pac_found, service); } -static bool match_ep_by_stream(const void *data, const void *user_data) +static bool match_setup_stream(const void *data, const void *user_data) +{ + const struct bap_setup *setup = data; + const struct bt_bap_stream *stream = user_data; + + return setup->stream == stream; +} + +static bool match_ep_stream(const void *data, const void *user_data) { const struct bap_ep *ep = data; const struct bt_bap_stream *stream = user_data; - return ep->stream == stream; + return queue_find(ep->setups, match_setup_stream, stream); } -static struct bap_ep *bap_find_ep_by_stream(struct bap_data *data, +static struct bap_setup *bap_find_setup_by_stream(struct bap_data *data, struct bt_bap_stream *stream) { - struct bap_ep *ep; + struct bap_ep *ep = NULL; switch (bt_bap_stream_get_type(stream)) { case BT_BAP_STREAM_TYPE_UCAST: - ep = queue_find(data->snks, match_ep_by_stream, stream); - if (ep) - return ep; + ep = queue_find(data->snks, match_ep_stream, stream); + if (!ep) + ep = queue_find(data->srcs, match_ep_stream, stream); - return queue_find(data->srcs, match_ep_by_stream, stream); + break; case BT_BAP_STREAM_TYPE_BCAST: - return queue_find(data->bcast, match_ep_by_stream, stream); + ep = queue_find(data->bcast, match_ep_stream, stream); + break; } + if (ep) + return queue_find(ep->setups, match_setup_stream, stream); + return NULL; } @@ -1435,8 +1476,9 @@ g_io_channel_shutdown(io, TRUE, NULL); } -static void bap_accept_io(struct bap_ep *ep, struct bt_bap_stream *stream, - int fd, int defer) +static void setup_accept_io(struct bap_setup *setup, + struct bt_bap_stream *stream, + int fd, int defer) { char c; struct pollfd pfd; @@ -1472,7 +1514,7 @@ } } - ep->cig_active = true; + setup->cig_active = true; return; @@ -1485,12 +1527,20 @@ uint8_t cig; }; +static bool match_cig_active(const void *data, const void *match_data) +{ + const struct bap_setup *setup = data; + const struct cig_busy_data *info = match_data; + + return (setup->qos.ucast.cig_id == info->cig) && setup->cig_active; +} + static bool cig_busy_ep(const void *data, const void *match_data) { const struct bap_ep *ep = data; const struct cig_busy_data *info = match_data; - return (ep->qos.ucast.cig_id == info->cig) && ep->cig_active; + return queue_find(ep->setups, match_cig_active, info); } static bool cig_busy_session(const void *data, const void *match_data) @@ -1518,32 +1568,40 @@ return queue_find(sessions, cig_busy_session, &info); } -static void bap_create_io(struct bap_data *data, struct bap_ep *ep, +static void setup_create_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, int defer); -static gboolean bap_io_recreate(void *user_data) +static gboolean setup_io_recreate(void *user_data) { - struct bap_ep *ep = user_data; + struct bap_setup *setup = user_data; - DBG("ep %p", ep); + DBG("%p", setup); - ep->io_id = 0; + setup->io_id = 0; - bap_create_io(ep->data, ep, ep->stream, true); + setup_create_io(setup->ep->data, setup, setup->stream, true); return FALSE; } -static void recreate_cig_ep(void *data, void *match_data) +static void setup_recreate(void *data, void *match_data) { - struct bap_ep *ep = (struct bap_ep *)data; + struct bap_setup *setup = data; struct cig_busy_data *info = match_data; - if (ep->qos.ucast.cig_id != info->cig || !ep->recreate || ep->io_id) + if (setup->qos.ucast.cig_id != info->cig || !setup->recreate || + setup->io_id) return; - ep->recreate = false; - ep->io_id = g_idle_add(bap_io_recreate, ep); + setup->recreate = false; + setup->io_id = g_idle_add(setup_io_recreate, setup); +} + +static void recreate_cig_ep(void *data, void *match_data) +{ + struct bap_ep *ep = data; + + queue_foreach(ep->setups, setup_recreate, match_data); } static void recreate_cig_session(void *data, void *match_data) @@ -1558,38 +1616,39 @@ queue_foreach(session->srcs, recreate_cig_ep, match_data); } -static void recreate_cig(struct bap_ep *ep) +static void recreate_cig(struct bap_setup *setup) { - struct bap_data *data = ep->data; + struct bap_data *data = setup->ep->data; struct cig_busy_data info; info.adapter = device_get_adapter(data->device); - info.cig = ep->qos.ucast.cig_id; + info.cig = setup->qos.ucast.cig_id; - DBG("adapter %p ep %p recreate CIG %d", info.adapter, ep, info.cig); + DBG("adapter %p setup %p recreate CIG %d", info.adapter, setup, + info.cig); - if (ep->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET) { - recreate_cig_ep(ep, &info); + if (setup->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET) { + recreate_cig_ep(setup->ep, &info); return; } queue_foreach(sessions, recreate_cig_session, &info); } -static gboolean bap_io_disconnected(GIOChannel *io, GIOCondition cond, +static gboolean setup_io_disconnected(GIOChannel *io, GIOCondition cond, gpointer user_data) { - struct bap_ep *ep = user_data; + struct bap_setup *setup = user_data; - DBG("ep %p recreate %s", ep, ep->recreate ? "true" : "false"); + DBG("%p recreate %s", setup, setup->recreate ? "true" : "false"); - ep->io_id = 0; + setup->io_id = 0; - bap_io_close(ep); + setup_io_close(setup, NULL); /* Check if connecting recreate IO */ - if (!is_cig_busy(ep->data, ep->qos.ucast.cig_id)) - recreate_cig(ep); + if (!is_cig_busy(setup->ep->data, setup->qos.ucast.cig_id)) + recreate_cig(setup); return FALSE; } @@ -1597,25 +1656,25 @@ static void bap_connect_bcast_io_cb(GIOChannel *chan, GError *err, gpointer user_data) { - struct bap_ep *ep = user_data; + struct bap_setup *setup = user_data; - if (!ep->stream) + if (!setup->stream) return; - iso_connect_bcast_cb(chan, err, ep->stream); + iso_connect_bcast_cb(chan, err, setup->stream); } static void bap_connect_io_cb(GIOChannel *chan, GError *err, gpointer user_data) { - struct bap_ep *ep = user_data; + struct bap_setup *setup = user_data; - if (!ep->stream) + if (!setup->stream) return; - iso_connect_cb(chan, err, ep->stream); + iso_connect_cb(chan, err, setup->stream); } -static void bap_connect_io(struct bap_data *data, struct bap_ep *ep, +static void setup_connect_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, struct bt_iso_qos *qos, int defer) { @@ -1626,39 +1685,40 @@ /* If IO already set skip creating it again */ if (bt_bap_stream_get_io(stream)) { - DBG("ep %p stream %p has existing io", ep, stream); + DBG("setup %p stream %p has existing io", setup, stream); return; } if (bt_bap_stream_io_is_connecting(stream, &fd)) { - bap_accept_io(ep, stream, fd, defer); + setup_accept_io(setup, stream, fd, defer); return; } /* If IO channel still up or CIG is busy, wait for it to be * disconnected and then recreate. */ - if (ep->io || is_cig_busy(data, ep->qos.ucast.cig_id)) { - DBG("ep %p stream %p defer %s wait recreate", ep, stream, + if (setup->io || is_cig_busy(data, setup->qos.ucast.cig_id)) { + DBG("setup %p stream %p defer %s wait recreate", setup, stream, defer ? "true" : "false"); - ep->recreate = true; + setup->recreate = true; return; } - if (ep->io_id) { - g_source_remove(ep->io_id); - ep->io_id = 0; + if (setup->io_id) { + g_source_remove(setup->io_id); + setup->io_id = 0; } - DBG("ep %p stream %p defer %s", ep, stream, defer ? "true" : "false"); + DBG("setup %p stream %p defer %s", setup, stream, + defer ? "true" : "false"); - io = bt_io_connect(bap_connect_io_cb, ep, NULL, &err, + io = bt_io_connect(bap_connect_io_cb, setup, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_DEST_BDADDR, - device_get_address(ep->data->device), + device_get_address(data->device), BT_IO_OPT_DEST_TYPE, - device_get_le_address_type(ep->data->device), + device_get_le_address_type(data->device), BT_IO_OPT_MODE, BT_IO_MODE_ISO, BT_IO_OPT_QOS, qos, BT_IO_OPT_DEFER_TIMEOUT, defer, @@ -1669,18 +1729,19 @@ return; } - ep->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, - bap_io_disconnected, ep); + setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + setup_io_disconnected, setup); - ep->io = io; - ep->cig_active = !defer; + setup->io = io; + setup->cig_active = !defer; bt_bap_stream_io_connecting(stream, g_io_channel_unix_get_fd(io)); } -static void bap_connect_io_broadcast(struct bap_data *data, struct bap_ep *ep, - struct bt_bap_stream *stream, - struct bt_iso_qos *qos) +static void setup_connect_io_broadcast(struct bap_data *data, + struct bap_setup *setup, + struct bt_bap_stream *stream, + struct bt_iso_qos *qos) { struct btd_adapter *adapter = data->user_data; GIOChannel *io = NULL; @@ -1695,18 +1756,19 @@ if (bt_bap_stream_get_io(stream)) return; - if (ep->io_id) { - g_source_remove(ep->io_id); - ep->io_id = 0; + if (setup->io_id) { + g_source_remove(setup->io_id); + setup->io_id = 0; } - base.base_len = ep->base->iov_len; + base.base_len = setup->base->iov_len; memset(base.base, 0, 248); - memcpy(base.base, ep->base->iov_base, ep->base->iov_len); - DBG("ep %p stream %p ", ep, stream); + memcpy(base.base, setup->base->iov_base, setup->base->iov_len); ba2str(btd_adapter_get_address(adapter), addr); - io = bt_io_connect(bap_connect_bcast_io_cb, ep, NULL, &err, + DBG("setup %p stream %p", setup, stream); + + io = bt_io_connect(bap_connect_bcast_io_cb, setup, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), BT_IO_OPT_DEST_BDADDR, @@ -1725,15 +1787,15 @@ return; } - ep->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, - bap_io_disconnected, ep); + setup->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + setup_io_disconnected, setup); - ep->io = io; + setup->io = io; bt_bap_stream_io_connecting(stream, g_io_channel_unix_get_fd(io)); } -static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream, +static void setup_listen_io(struct bap_data *data, struct bt_bap_stream *stream, struct bt_iso_qos *qos) { struct btd_adapter *adapter = device_get_adapter(data->device); @@ -1765,8 +1827,10 @@ data->listen_io = io; } -static void bap_listen_io_broadcast(struct bap_data *data, struct bap_ep *ep, - struct bt_bap_stream *stream, struct bt_iso_qos *qos) +static void setup_listen_io_broadcast(struct bap_data *data, + struct bap_setup *setup, + struct bt_bap_stream *stream, + struct bt_iso_qos *qos) { GIOChannel *io; GError *err = NULL; @@ -1784,9 +1848,9 @@ if (bt_bap_stream_get_io(stream) || data->listen_io) return; - io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, ep->data, NULL, &err, + io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, setup, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, - btd_adapter_get_address(ep->data->adapter), + btd_adapter_get_address(data->adapter), BT_IO_OPT_DEST_BDADDR, device_get_address(data->device), BT_IO_OPT_DEST_TYPE, @@ -1800,12 +1864,14 @@ error("%s", err->message); g_error_free(err); } - ep->io = io; - ep->data->listen_io = io; + setup->io = io; + data->listen_io = io; } -static void bap_create_ucast_io(struct bap_data *data, struct bap_ep *ep, - struct bt_bap_stream *stream, int defer) +static void setup_create_ucast_io(struct bap_data *data, + struct bap_setup *setup, + struct bt_bap_stream *stream, + int defer) { struct bt_bap_qos *qos[2] = {}; struct bt_iso_qos iso_qos; @@ -1825,14 +1891,15 @@ bap_iso_qos(qos[0], &iso_qos.ucast.in); bap_iso_qos(qos[1], &iso_qos.ucast.out); - if (ep) - bap_connect_io(data, ep, stream, &iso_qos, defer); + if (setup) + setup_connect_io(data, setup, stream, &iso_qos, defer); else - bap_listen_io(data, stream, &iso_qos); + setup_listen_io(data, stream, &iso_qos); } -static void bap_create_bcast_io(struct bap_data *data, struct bap_ep *ep, - struct bt_bap_stream *stream, int defer) +static void setup_create_bcast_io(struct bap_data *data, + struct bap_setup *setup, + struct bt_bap_stream *stream, int defer) { struct bt_iso_qos iso_qos; @@ -1841,33 +1908,35 @@ if (!defer) goto done; - iso_qos.bcast.big = ep->qos.bcast.big; - iso_qos.bcast.bis = ep->qos.bcast.bis; - iso_qos.bcast.sync_factor = ep->qos.bcast.sync_factor; - iso_qos.bcast.packing = ep->qos.bcast.packing; - iso_qos.bcast.framing = ep->qos.bcast.framing; - iso_qos.bcast.encryption = ep->qos.bcast.encryption; - if (ep->qos.bcast.bcode) - memcpy(iso_qos.bcast.bcode, ep->qos.bcast.bcode->iov_base, 16); - iso_qos.bcast.options = ep->qos.bcast.options; - iso_qos.bcast.skip = ep->qos.bcast.skip; - iso_qos.bcast.sync_timeout = ep->qos.bcast.sync_timeout; - iso_qos.bcast.sync_cte_type = ep->qos.bcast.sync_cte_type; - iso_qos.bcast.mse = ep->qos.bcast.mse; - iso_qos.bcast.timeout = ep->qos.bcast.timeout; - memcpy(&iso_qos.bcast.out, &ep->qos.bcast.io_qos, + iso_qos.bcast.big = setup->qos.bcast.big; + iso_qos.bcast.bis = setup->qos.bcast.bis; + iso_qos.bcast.sync_factor = setup->qos.bcast.sync_factor; + iso_qos.bcast.packing = setup->qos.bcast.packing; + iso_qos.bcast.framing = setup->qos.bcast.framing; + iso_qos.bcast.encryption = setup->qos.bcast.encryption; + if (setup->qos.bcast.bcode) + memcpy(iso_qos.bcast.bcode, setup->qos.bcast.bcode->iov_base, + 16); + iso_qos.bcast.options = setup->qos.bcast.options; + iso_qos.bcast.skip = setup->qos.bcast.skip; + iso_qos.bcast.sync_timeout = setup->qos.bcast.sync_timeout; + iso_qos.bcast.sync_cte_type = setup->qos.bcast.sync_cte_type; + iso_qos.bcast.mse = setup->qos.bcast.mse; + iso_qos.bcast.timeout = setup->qos.bcast.timeout; + memcpy(&iso_qos.bcast.out, &setup->qos.bcast.io_qos, sizeof(struct bt_iso_io_qos)); done: - if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) - bap_connect_io_broadcast(data, ep, stream, &iso_qos); + if (bt_bap_pac_get_type(setup->ep->lpac) == BT_BAP_BCAST_SOURCE) + setup_connect_io_broadcast(data, setup, stream, &iso_qos); else - bap_listen_io_broadcast(data, ep, stream, &iso_qos); + setup_listen_io_broadcast(data, setup, stream, &iso_qos); } -static void bap_create_io(struct bap_data *data, struct bap_ep *ep, +static void setup_create_io(struct bap_data *data, struct bap_setup *setup, struct bt_bap_stream *stream, int defer) { - DBG("ep %p stream %p defer %s", ep, stream, defer ? "true" : "false"); + DBG("setup %p stream %p defer %s", setup, stream, + defer ? "true" : "false"); if (!data->streams) data->streams = queue_new(); @@ -1877,10 +1946,10 @@ switch (bt_bap_stream_get_type(stream)) { case BT_BAP_STREAM_TYPE_UCAST: - bap_create_ucast_io(data, ep, stream, defer); + setup_create_ucast_io(data, setup, stream, defer); break; case BT_BAP_STREAM_TYPE_BCAST: - bap_create_bcast_io(data, ep, stream, defer); + setup_create_bcast_io(data, setup, stream, defer); break; } } @@ -1889,7 +1958,7 @@ uint8_t new_state, void *user_data) { struct bap_data *data = user_data; - struct bap_ep *ep; + struct bap_setup *setup; DBG("stream %p: %s(%u) -> %s(%u)", stream, bt_bap_stream_statestr(old_state), old_state, @@ -1902,21 +1971,20 @@ if (new_state == old_state && new_state != BT_BAP_STREAM_STATE_CONFIG) return; - ep = bap_find_ep_by_stream(data, stream); + setup = bap_find_setup_by_stream(data, stream); switch (new_state) { case BT_BAP_STREAM_STATE_IDLE: /* Release stream if idle */ - if (ep) { - bap_io_close(ep); - ep->stream = NULL; - } else + if (setup) + setup_free(setup); + else queue_remove(data->streams, stream); break; case BT_BAP_STREAM_STATE_CONFIG: - if (ep && !ep->id) { - bap_create_io(data, ep, stream, true); - if (!ep->io) { + if (setup && !setup->id) { + setup_create_io(data, setup, stream, true); + if (!setup->io) { error("Unable to create io"); if (old_state != BT_BAP_STREAM_STATE_RELEASING) bt_bap_stream_release(stream, NULL, @@ -1927,9 +1995,10 @@ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST) { /* Wait QoS response to respond */ - ep->id = bt_bap_stream_qos(stream, &ep->qos, - qos_cb, ep); - if (!ep->id) { + setup->id = bt_bap_stream_qos(stream, + &setup->qos, + qos_cb, setup); + if (!setup->id) { error("Failed to Configure QoS"); bt_bap_stream_release(stream, NULL, NULL); @@ -1940,12 +2009,12 @@ case BT_BAP_STREAM_STATE_QOS: if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST) { - bap_create_io(data, ep, stream, true); + setup_create_io(data, setup, stream, true); } break; case BT_BAP_STREAM_STATE_ENABLING: - if (ep) - bap_create_io(data, ep, stream, false); + if (setup) + setup_create_io(data, setup, stream, false); break; case BT_BAP_STREAM_STATE_STREAMING: break; @@ -2117,66 +2186,69 @@ void *user_data) { struct bap_data *data = user_data; - struct bap_ep *ep; + struct bap_setup *setup; + struct bt_bap_qos *qos; GIOChannel *io; if (!state) return; - ep = bap_find_ep_by_stream(data, stream); - if (!ep) + setup = bap_find_setup_by_stream(data, stream); + if (!setup) return; - ep->recreate = false; + setup->recreate = false; + qos = &setup->qos; - if (!ep->io) { + if (!setup->io) { io = g_io_channel_unix_new(fd); - ep->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, - bap_io_disconnected, ep); - ep->io = io; + setup->io_id = g_io_add_watch(io, + G_IO_HUP | G_IO_ERR | G_IO_NVAL, + setup_io_disconnected, setup); + setup->io = io; } else - io = ep->io; + io = setup->io; g_io_channel_set_close_on_unref(io, FALSE); - switch (bt_bap_stream_get_type(ep->stream)) { + switch (bt_bap_stream_get_type(setup->stream)) { case BT_BAP_STREAM_TYPE_UCAST: /* Attempt to get CIG/CIS if they have not been set */ - if (ep->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET || - ep->qos.ucast.cis_id == BT_ISO_QOS_CIS_UNSET) { - struct bt_iso_qos qos; + if (qos->ucast.cig_id == BT_ISO_QOS_CIG_UNSET || + qos->ucast.cis_id == BT_ISO_QOS_CIS_UNSET) { + struct bt_iso_qos iso_qos; - if (!io_get_qos(io, &qos)) { + if (!io_get_qos(io, &iso_qos)) { g_io_channel_unref(io); return; } - ep->qos.ucast.cig_id = qos.ucast.cig; - ep->qos.ucast.cis_id = qos.ucast.cis; + qos->ucast.cig_id = iso_qos.ucast.cig; + qos->ucast.cis_id = iso_qos.ucast.cis; } DBG("stream %p fd %d: CIG 0x%02x CIS 0x%02x", stream, fd, - ep->qos.ucast.cig_id, ep->qos.ucast.cis_id); + qos->ucast.cig_id, qos->ucast.cis_id); break; case BT_BAP_STREAM_TYPE_BCAST: /* Attempt to get BIG/BIS if they have not been set */ - if (ep->qos.bcast.big == BT_ISO_QOS_BIG_UNSET || - ep->qos.bcast.bis == BT_ISO_QOS_BIS_UNSET) { - struct bt_iso_qos qos; + if (setup->qos.bcast.big == BT_ISO_QOS_BIG_UNSET || + setup->qos.bcast.bis == BT_ISO_QOS_BIS_UNSET) { + struct bt_iso_qos iso_qos; - if (!io_get_qos(io, &qos)) { + if (!io_get_qos(io, &iso_qos)) { g_io_channel_unref(io); return; } - ep->qos.bcast.big = qos.bcast.big; - ep->qos.bcast.bis = qos.bcast.bis; - bt_bap_stream_config(ep->stream, &ep->qos, - ep->caps, NULL, NULL); + qos->bcast.big = iso_qos.bcast.big; + qos->bcast.bis = iso_qos.bcast.bis; + bt_bap_stream_config(setup->stream, qos, setup->caps, + NULL, NULL); } DBG("stream %p fd %d: BIG 0x%02x BIS 0x%02x", stream, fd, - ep->qos.bcast.big, ep->qos.bcast.bis); + qos->bcast.big, qos->bcast.bis); } } diff -Nru bluez-5.71/profiles/audio/media.c bluez-5.72/profiles/audio/media.c --- bluez-5.71/profiles/audio/media.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/profiles/audio/media.c 2024-01-13 06:25:53.000000000 +0800 @@ -921,7 +921,7 @@ } static int pac_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, - struct bt_bap_pac_qos *qos, + uint32_t location, struct bt_bap_pac_qos *qos, bt_bap_pac_select_t cb, void *cb_data, void *user_data) { struct media_endpoint *endpoint = user_data; @@ -969,6 +969,10 @@ g_dbus_dict_append_entry(&dict, "Locations", DBUS_TYPE_UINT32, &loc); + if (location) + g_dbus_dict_append_entry(&dict, "ChannelAllocation", + DBUS_TYPE_UINT32, &location); + if (metadata) { key = "Metadata"; g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &key, diff -Nru bluez-5.71/profiles/audio/transport.c bluez-5.72/profiles/audio/transport.c --- bluez-5.71/profiles/audio/transport.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/profiles/audio/transport.c 2024-01-13 06:25:53.000000000 +0800 @@ -79,6 +79,7 @@ struct avdtp *session; uint16_t delay; int8_t volume; + guint watch; }; struct bap_transport { @@ -89,6 +90,27 @@ guint resume_id; }; +struct media_transport_ops { + const char *uuid; + const GDBusPropertyTable *properties; + void (*set_owner)(struct media_transport *transport, + struct media_owner *owner); + void (*remove_owner)(struct media_transport *transport, + struct media_owner *owner); + void *(*init)(struct media_transport *transport, void *stream); + guint (*resume)(struct media_transport *transport, + struct media_owner *owner); + guint (*suspend)(struct media_transport *transport, + struct media_owner *owner); + void (*cancel)(struct media_transport *transport, guint id); + void (*set_state)(struct media_transport *transport, + transport_state_t state); + void *(*get_stream)(struct media_transport *transport); + int8_t (*get_volume)(struct media_transport *transport); + int (*set_volume)(struct media_transport *transport, int8_t level); + GDestroyNotify destroy; +}; + struct media_transport { char *path; /* Transport object path */ struct btd_device *device; /* Transport device */ @@ -102,20 +124,7 @@ uint16_t imtu; /* Transport input mtu */ uint16_t omtu; /* Transport output mtu */ transport_state_t state; - guint hs_watch; - guint source_watch; - guint sink_watch; - guint (*resume) (struct media_transport *transport, - struct media_owner *owner); - guint (*suspend) (struct media_transport *transport, - struct media_owner *owner); - void (*cancel) (struct media_transport *transport, - guint id); - void (*set_state) (struct media_transport *transport, - transport_state_t state); - void *(*get_stream) - (struct media_transport *transport); - GDestroyNotify destroy; + struct media_transport_ops *ops; void *data; }; @@ -198,20 +207,14 @@ "State"); /* Update transport specific data */ - if (transport->set_state) - transport->set_state(transport, state); + if (transport->ops && transport->ops->set_state) + transport->ops->set_state(transport, state); } void media_transport_destroy(struct media_transport *transport) { char *path; - if (transport->sink_watch) - sink_remove_state_cb(transport->sink_watch); - - if (transport->source_watch) - source_remove_state_cb(transport->source_watch); - path = g_strdup(transport->path); g_dbus_unregister_interface(btd_get_dbus_connection(), path, MEDIA_TRANSPORT_INTERFACE); @@ -261,8 +264,8 @@ DBG("Owner %s Request %s", owner->name, dbus_message_get_member(req->msg)); - if (req->id) - transport->cancel(transport, req->id); + if (req->id && transport->ops && transport->ops->cancel) + transport->ops->cancel(transport, req->id); owner->pending = NULL; if (req->msg) @@ -297,10 +300,36 @@ transport->owner = NULL; } +static guint media_transport_suspend(struct media_transport *transport, + struct media_owner *owner) +{ + if (!state_in_use(transport->state)) + return 0; + + DBG("Transport %s Owner %s", transport->path, owner ? owner->name : ""); + + if (transport->ops && transport->ops->suspend) + return transport->ops->suspend(transport, owner); + + return 0; +} + +static void transport_bap_remove_owner(struct media_transport *transport, + struct media_owner *owner) +{ + struct bap_transport *bap = transport->data; + + if (bap && bap->linked) { + struct bt_bap_stream *link; + + link = bt_bap_stream_io_get_link(bap->stream); + linked_transport_remove_owner(link, owner); + } +} + static void media_transport_remove_owner(struct media_transport *transport) { struct media_owner *owner = transport->owner; - struct bap_transport *bap = transport->data; if (!transport->owner) return; @@ -312,17 +341,16 @@ media_request_reply(owner->pending, EIO); transport->owner = NULL; - if (bap->linked) - queue_foreach(bt_bap_stream_io_get_links(bap->stream), - linked_transport_remove_owner, owner); + + if (transport->ops && transport->ops->remove_owner) + transport->ops->remove_owner(transport, owner); if (owner->watch) g_dbus_remove_watch(btd_get_dbus_connection(), owner->watch); media_owner_free(owner); - if (state_in_use(transport->state)) - transport->suspend(transport, NULL); + media_transport_suspend(transport, NULL); } static gboolean media_transport_set_fd(struct media_transport *transport, @@ -385,7 +413,7 @@ media_transport_remove_owner(transport); } -static guint resume_a2dp(struct media_transport *transport, +static guint transport_a2dp_resume(struct media_transport *transport, struct media_owner *owner) { struct a2dp_transport *a2dp = transport->data; @@ -439,7 +467,7 @@ media_transport_remove_owner(transport); } -static guint suspend_a2dp(struct media_transport *transport, +static guint transport_a2dp_suspend(struct media_transport *transport, struct media_owner *owner) { struct a2dp_transport *a2dp = transport->data; @@ -456,11 +484,49 @@ return 0; } -static void cancel_a2dp(struct media_transport *transport, guint id) +static void transport_a2dp_cancel(struct media_transport *transport, guint id) { a2dp_cancel(id); } +static int8_t transport_a2dp_get_volume(struct media_transport *transport) +{ + struct a2dp_transport *a2dp = transport->data; + return a2dp->volume; +} + +static int transport_a2dp_src_set_volume(struct media_transport *transport, + int8_t level) +{ + struct a2dp_transport *a2dp = transport->data; + + if (a2dp->volume == level) + return 0; + + return avrcp_set_volume(transport->device, level, false); +} + +static int transport_a2dp_snk_set_volume(struct media_transport *transport, + int8_t level) +{ + struct a2dp_transport *a2dp = transport->data; + bool notify; + + if (a2dp->volume == level) + return 0; + + notify = a2dp->watch ? true : false; + if (notify) { + a2dp->volume = level; + g_dbus_emit_property_changed(btd_get_dbus_connection(), + transport->path, + MEDIA_TRANSPORT_INTERFACE, + "Volume"); + } + + return avrcp_set_volume(transport->device, level, notify); +} + static void media_owner_exit(DBusConnection *connection, void *user_data) { struct media_owner *owner = user_data; @@ -488,17 +554,27 @@ transport->owner = owner; } -static void media_transport_set_owner(struct media_transport *transport, +static void transport_bap_set_owner(struct media_transport *transport, struct media_owner *owner) { struct bap_transport *bap = transport->data; + if (bap && bap->linked) { + struct bt_bap_stream *link; + + link = bt_bap_stream_io_get_link(bap->stream); + linked_transport_set_owner(link, owner); + } +} + +static void media_transport_set_owner(struct media_transport *transport, + struct media_owner *owner) +{ DBG("Transport %s Owner %s", transport->path, owner->name); transport->owner = owner; - if (bap->linked) - queue_foreach(bt_bap_stream_io_get_links(bap->stream), - linked_transport_set_owner, owner); + if (transport->ops && transport->ops->set_owner) + transport->ops->set_owner(transport, owner); owner->transport = transport; owner->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(), @@ -528,13 +604,24 @@ owner->pending = req; } -static void *get_stream_bap(struct media_transport *transport) +static void *transport_bap_get_stream(struct media_transport *transport) { struct bap_transport *bap = transport->data; return bap->stream; } +static guint media_transport_resume(struct media_transport *transport, + struct media_owner *owner) +{ + DBG("Transport %s Owner %s", transport->path, owner ? owner->name : ""); + + if (transport->ops && transport->ops->resume) + return transport->ops->resume(transport, owner); + + return 0; +} + static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -560,7 +647,7 @@ media_transport_set_owner(transport, owner); } - id = transport->resume(transport, owner); + id = media_transport_resume(transport, owner); if (id == 0) { media_owner_free(owner); return btd_error_not_authorized(msg); @@ -593,7 +680,7 @@ return btd_error_not_available(msg); owner = media_owner_create(msg); - id = transport->resume(transport, owner); + id = media_transport_resume(transport, owner); if (id == 0) { media_owner_free(owner); return btd_error_not_authorized(msg); @@ -611,8 +698,13 @@ void *user_data) { struct media_owner *owner = user_data; - struct media_request *req = owner->pending; - struct media_transport *transport = owner->transport; + struct media_request *req; + struct media_transport *transport; + + if (!owner) + return; + + req = owner->pending; /* Release always succeeds */ if (req) { @@ -621,8 +713,12 @@ media_owner_remove(owner); } - transport_set_state(transport, TRANSPORT_STATE_IDLE); - media_transport_remove_owner(transport); + transport = owner->transport; + + if (transport) { + transport_set_state(transport, TRANSPORT_STATE_IDLE); + media_transport_remove_owner(transport); + } } static void bap_disable_complete(struct bt_bap_stream *stream, @@ -637,7 +733,6 @@ { struct media_transport *transport = data; struct media_owner *owner = transport->owner; - struct bap_transport *bap = transport->data; const char *sender; struct media_request *req; guint id; @@ -660,7 +755,7 @@ transport_set_state(transport, TRANSPORT_STATE_SUSPENDING); - id = transport->suspend(transport, owner); + id = media_transport_suspend(transport, owner); if (id == 0) { media_transport_remove_owner(transport); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); @@ -669,11 +764,6 @@ req = media_request_create(msg, id); media_owner_add(owner, req); - if (bt_bap_stream_get_type(bap->stream) == - BT_BAP_STREAM_TYPE_BCAST) { - bap_disable_complete(bap->stream, 0x00, 0x00, owner); - } - return NULL; } @@ -767,32 +857,59 @@ static gboolean volume_exists(const GDBusPropertyTable *property, void *data) { struct media_transport *transport = data; - struct a2dp_transport *a2dp = transport->data; + int8_t volume; - return a2dp->volume >= 0; + if (media_transport_get_volume(transport, &volume)) + return FALSE; + + return volume >= 0; +} + +int media_transport_get_volume(struct media_transport *transport, + int8_t *volume) +{ + if (transport->ops && transport->ops->get_volume) { + *volume = transport->ops->get_volume(transport); + return 0; + } + + return -EINVAL; } static gboolean get_volume(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct media_transport *transport = data; - struct a2dp_transport *a2dp = transport->data; - uint16_t volume = (uint16_t)a2dp->volume; + int8_t level; + uint16_t volume; + + if (media_transport_get_volume(transport, &level)) + return FALSE; + + volume = level; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &volume); return TRUE; } +static int media_transport_set_volume(struct media_transport *transport, + int8_t level) +{ + DBG("Transport %s level %d", transport->path, level); + + if (transport->ops && transport->ops->set_volume) + return transport->ops->set_volume(transport, level); + + return 0; +} + static void set_volume(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { struct media_transport *transport = data; - struct a2dp_transport *a2dp = transport->data; uint16_t arg; - int8_t volume; - bool notify; int err; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) { @@ -810,26 +927,13 @@ return; } - volume = (int8_t)arg; - if (a2dp->volume == volume) - return; - - notify = transport->source_watch ? true : false; - if (notify) { - a2dp->volume = volume; - g_dbus_emit_property_changed(btd_get_dbus_connection(), - transport->path, - MEDIA_TRANSPORT_INTERFACE, - "Volume"); - } - - err = avrcp_set_volume(transport->device, volume, notify); + err = media_transport_set_volume(transport, arg); if (err) { - error("avrcp_set_volume returned %s (%d)", strerror(-err), err); + error("Unable to set volume: %s (%d)", strerror(-err), err); g_dbus_pending_property_error(id, - ERROR_INTERFACE ".Failed", - "Internal error %s (%d)", - strerror(-err), err); + ERROR_INTERFACE ".Failed", + "Internal error %s (%d)", + strerror(-err), err); return; } @@ -869,7 +973,7 @@ { }, }; -static const GDBusPropertyTable a2dp_properties[] = { +static const GDBusPropertyTable transport_a2dp_properties[] = { { "Device", "o", get_device }, { "UUID", "s", get_uuid }, { "Codec", "y", get_codec }, @@ -962,12 +1066,15 @@ return bap->linked; } -static void append_links(void *data, void *user_data) +static void append_link(void *data, void *user_data) { struct bt_bap_stream *stream = data; DBusMessageIter *array = user_data; struct media_transport *transport; + if (!stream) + return; + transport = find_transport_by_bap_stream(stream); if (!transport) { error("Unable to find transport"); @@ -983,14 +1090,14 @@ { struct media_transport *transport = data; struct bap_transport *bap = transport->data; - struct queue *links = bt_bap_stream_io_get_links(bap->stream); + struct bt_bap_stream *link = bt_bap_stream_io_get_link(bap->stream); DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH_AS_STRING, &array); - queue_foreach(links, append_links, &array); + append_link(link, &array); dbus_message_iter_close_container(iter, &array); @@ -1005,7 +1112,7 @@ return bap->qos.ucast.io_qos.phy != 0x00; } -static const GDBusPropertyTable bap_ucast_properties[] = { +static const GDBusPropertyTable transport_bap_uc_properties[] = { { "Device", "o", get_device }, { "UUID", "s", get_uuid }, { "Codec", "y", get_codec }, @@ -1075,7 +1182,7 @@ return bap->qos.bcast.io_qos.phy != 0x00; } -static const GDBusPropertyTable bap_bcast_properties[] = { +static const GDBusPropertyTable transport_bap_bc_properties[] = { { "Device", "o", get_device }, { "UUID", "s", get_uuid }, { "Codec", "y", get_codec }, @@ -1088,14 +1195,34 @@ { } }; -static void destroy_a2dp(void *data) +static void transport_a2dp_destroy(void *data) { struct a2dp_transport *a2dp = data; if (a2dp->session) avdtp_unref(a2dp->session); - g_free(a2dp); + free(a2dp); +} + +static void transport_a2dp_src_destroy(void *data) +{ + struct a2dp_transport *a2dp = data; + + if (a2dp->watch) + sink_remove_state_cb(a2dp->watch); + + transport_a2dp_destroy(data); +} + +static void transport_a2dp_snk_destroy(void *data) +{ + struct a2dp_transport *a2dp = data; + + if (a2dp->watch) + source_remove_state_cb(a2dp->watch); + + transport_a2dp_destroy(data); } static void media_transport_free(void *data) @@ -1107,8 +1234,8 @@ if (transport->owner) media_transport_remove_owner(transport); - if (transport->destroy != NULL) - transport->destroy(transport->data); + if (transport->ops && transport->ops->destroy) + transport->ops->destroy(transport->data); g_free(transport->configuration); g_free(transport->path); @@ -1159,53 +1286,39 @@ transport_update_playing(transport, FALSE); } -static int media_transport_init_source(struct media_transport *transport) +static void *transport_a2dp_src_init(struct media_transport *transport, + void *stream) { struct btd_service *service; struct a2dp_transport *a2dp; service = btd_device_get_service(transport->device, A2DP_SINK_UUID); - if (service == NULL) - return -EINVAL; - - a2dp = g_new0(struct a2dp_transport, 1); - - transport->resume = resume_a2dp; - transport->suspend = suspend_a2dp; - transport->cancel = cancel_a2dp; - transport->data = a2dp; - transport->destroy = destroy_a2dp; + if (!service) + return NULL; + a2dp = new0(struct a2dp_transport, 1); a2dp->volume = -1; - transport->sink_watch = sink_add_state_cb(service, sink_state_changed, - transport); + a2dp->watch = sink_add_state_cb(service, sink_state_changed, transport); - return 0; + return a2dp; } -static int media_transport_init_sink(struct media_transport *transport) +static void *transport_a2dp_snk_init(struct media_transport *transport, + void *stream) { struct btd_service *service; struct a2dp_transport *a2dp; service = btd_device_get_service(transport->device, A2DP_SOURCE_UUID); - if (service == NULL) - return -EINVAL; - - a2dp = g_new0(struct a2dp_transport, 1); - - transport->resume = resume_a2dp; - transport->suspend = suspend_a2dp; - transport->cancel = cancel_a2dp; - transport->data = a2dp; - transport->destroy = destroy_a2dp; + if (!service) + return NULL; + a2dp = new0(struct a2dp_transport, 1); a2dp->volume = 127; - transport->source_watch = source_add_state_cb(service, - source_state_changed, - transport); + a2dp->watch = source_add_state_cb(service, source_state_changed, + transport); - return 0; + return a2dp; } static void bap_enable_complete(struct bt_bap_stream *stream, @@ -1280,15 +1393,15 @@ static void bap_update_links(const struct media_transport *transport) { struct bap_transport *bap = transport->data; - struct queue *links = bt_bap_stream_io_get_links(bap->stream); + struct bt_bap_stream *link = bt_bap_stream_io_get_link(bap->stream); - if (bap->linked == !queue_isempty(links)) + if (bap->linked == (!!link)) return; - bap->linked = !queue_isempty(links); + bap->linked = link ? true : false; /* Check if the links transport has been create yet */ - if (bap->linked && !queue_find(links, match_link_transport, NULL)) { + if (bap->linked && !match_link_transport(link, NULL)) { bap->linked = false; return; } @@ -1366,7 +1479,7 @@ "Configuration"); } -static guint resume_bap(struct media_transport *transport, +static guint transport_bap_resume(struct media_transport *transport, struct media_owner *owner) { struct bap_transport *bap = transport->data; @@ -1402,11 +1515,12 @@ return id; } -static guint suspend_bap(struct media_transport *transport, +static guint transport_bap_suspend(struct media_transport *transport, struct media_owner *owner) { struct bap_transport *bap = transport->data; bt_bap_stream_func_t func = NULL; + guint id; if (!bap->stream) return 0; @@ -1418,10 +1532,17 @@ bap_update_links(transport); - return bt_bap_stream_disable(bap->stream, bap->linked, func, owner); + id = bt_bap_stream_disable(bap->stream, bap->linked, func, owner); + + if (bt_bap_stream_get_type(bap->stream) == BT_BAP_STREAM_TYPE_BCAST) { + bap_disable_complete(bap->stream, 0x00, 0x00, owner); + return 0; + } + + return id; } -static void cancel_bap(struct media_transport *transport, guint id) +static void transport_bap_cancel(struct media_transport *transport, guint id) { struct bap_transport *bap = transport->data; @@ -1452,17 +1573,19 @@ transport_set_state(transport, state); } -static void set_state_bap(struct media_transport *transport, +static void transport_bap_set_state(struct media_transport *transport, transport_state_t state) { struct bap_transport *bap = transport->data; + struct bt_bap_stream *link; if (!bap->linked) return; - /* Update links */ - queue_foreach(bt_bap_stream_io_get_links(bap->stream), link_set_state, - UINT_TO_PTR(state)); + link = bt_bap_stream_io_get_link(bap->stream); + + /* Update link */ + link_set_state(link, UINT_TO_PTR(state)); } static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state, @@ -1558,7 +1681,7 @@ bap_update_links(transport); } -static void free_bap(void *data) +static void transport_bap_destroy(void *data) { struct bap_transport *bap = data; @@ -1567,8 +1690,7 @@ free(bap); } -static int media_transport_init_bap(struct media_transport *transport, - void *stream) +static void *transport_bap_init(struct media_transport *transport, void *stream) { struct bt_bap_qos *qos; struct bap_transport *bap; @@ -1583,15 +1705,75 @@ bap_connecting, transport, NULL); - transport->data = bap; - transport->resume = resume_bap; - transport->suspend = suspend_bap; - transport->cancel = cancel_bap; - transport->set_state = set_state_bap; - transport->get_stream = get_stream_bap; - transport->destroy = free_bap; + return bap; +} - return 0; +#define TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner, _init, \ + _resume, _suspend, _cancel, _set_state, _get_stream, \ + _get_volume, _set_volume, _destroy) \ +{ \ + .uuid = _uuid, \ + .properties = _props, \ + .set_owner = _set_owner, \ + .remove_owner = _remove_owner, \ + .init = _init, \ + .resume = _resume, \ + .suspend = _suspend, \ + .cancel = _cancel, \ + .set_state = _set_state, \ + .get_stream = _get_stream, \ + .get_volume = _get_volume, \ + .set_volume = _set_volume, \ + .destroy = _destroy \ +} + +#define A2DP_OPS(_uuid, _init, _set_volume, _destroy) \ + TRANSPORT_OPS(_uuid, transport_a2dp_properties, NULL, NULL, _init, \ + transport_a2dp_resume, transport_a2dp_suspend, \ + transport_a2dp_cancel, NULL, NULL, \ + transport_a2dp_get_volume, _set_volume, \ + _destroy) + +#define BAP_OPS(_uuid, _props, _set_owner, _remove_owner) \ + TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner,\ + transport_bap_init, \ + transport_bap_resume, transport_bap_suspend, \ + transport_bap_cancel, transport_bap_set_state, \ + transport_bap_get_stream, NULL, NULL, \ + transport_bap_destroy) + +#define BAP_UC_OPS(_uuid) \ + BAP_OPS(_uuid, transport_bap_uc_properties, \ + transport_bap_set_owner, transport_bap_remove_owner) + +#define BAP_BC_OPS(_uuid) \ + BAP_OPS(_uuid, transport_bap_bc_properties, NULL, NULL) + +static struct media_transport_ops transport_ops[] = { + A2DP_OPS(A2DP_SOURCE_UUID, transport_a2dp_src_init, + transport_a2dp_src_set_volume, + transport_a2dp_src_destroy), + A2DP_OPS(A2DP_SINK_UUID, transport_a2dp_snk_init, + transport_a2dp_snk_set_volume, + transport_a2dp_snk_destroy), + BAP_UC_OPS(PAC_SOURCE_UUID), + BAP_UC_OPS(PAC_SINK_UUID), + BAP_BC_OPS(BCAA_SERVICE_UUID), + BAP_BC_OPS(BAA_SERVICE_UUID), +}; + +static struct media_transport_ops *media_transport_find_ops(const char *uuid) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(transport_ops); i++) { + struct media_transport_ops *ops = &transport_ops[i]; + + if (!strcasecmp(uuid, ops->uuid)) + return ops; + } + + return NULL; } struct media_transport *media_transport_create(struct btd_device *device, @@ -1602,9 +1784,8 @@ { struct media_endpoint *endpoint = data; struct media_transport *transport; - const char *uuid; + struct media_transport_ops *ops; static int fd = 0; - const GDBusPropertyTable *properties; transport = g_new0(struct media_transport, 1); if (device) @@ -1629,31 +1810,21 @@ fd++); transport->fd = -1; - uuid = media_endpoint_get_uuid(endpoint); - if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) { - if (media_transport_init_source(transport) < 0) - goto fail; - properties = a2dp_properties; - } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) { - if (media_transport_init_sink(transport) < 0) - goto fail; - properties = a2dp_properties; - } else if (!strcasecmp(uuid, PAC_SINK_UUID) || - !strcasecmp(uuid, PAC_SOURCE_UUID)) { - if (media_transport_init_bap(transport, stream) < 0) - goto fail; - properties = bap_ucast_properties; - } else if (!strcasecmp(uuid, BCAA_SERVICE_UUID) || - !strcasecmp(uuid, BAA_SERVICE_UUID)) { - if (media_transport_init_bap(transport, stream) < 0) - goto fail; - properties = bap_bcast_properties; - } else + ops = media_transport_find_ops(media_endpoint_get_uuid(endpoint)); + if (!ops) goto fail; + transport->ops = ops; + + if (ops->init) { + transport->data = ops->init(transport, stream); + if (!transport->data) + goto fail; + } + if (g_dbus_register_interface(btd_get_dbus_connection(), transport->path, MEDIA_TRANSPORT_INTERFACE, - transport_methods, NULL, properties, + transport_methods, NULL, ops->properties, transport, media_transport_free) == FALSE) { error("Could not register transport %s", transport->path); goto fail; @@ -1675,8 +1846,8 @@ void *media_transport_get_stream(struct media_transport *transport) { - if (transport->get_stream) - return transport->get_stream(transport); + if (transport->ops && transport->ops->get_stream) + return transport->ops->get_stream(transport); return NULL; } @@ -1702,12 +1873,6 @@ return transport->device; } -int8_t media_transport_get_volume(struct media_transport *transport) -{ - struct a2dp_transport *a2dp = transport->data; - return a2dp->volume; -} - void media_transport_update_volume(struct media_transport *transport, int8_t volume) { @@ -1741,8 +1906,14 @@ continue; /* Volume is A2DP only */ - if (media_endpoint_get_sep(transport->endpoint)) - return media_transport_get_volume(transport); + if (media_endpoint_get_sep(transport->endpoint)) { + int8_t volume; + + if (!media_transport_get_volume(transport, &volume)) + return volume; + + return -1; + } } /* If transport volume doesn't exists use device_volume */ diff -Nru bluez-5.71/profiles/audio/transport.h bluez-5.72/profiles/audio/transport.h --- bluez-5.71/profiles/audio/transport.h 2023-06-30 16:10:20.000000000 +0800 +++ bluez-5.72/profiles/audio/transport.h 2024-01-13 06:25:53.000000000 +0800 @@ -21,7 +21,8 @@ const char *media_transport_get_path(struct media_transport *transport); void *media_transport_get_stream(struct media_transport *transport); struct btd_device *media_transport_get_dev(struct media_transport *transport); -int8_t media_transport_get_volume(struct media_transport *transport); +int media_transport_get_volume(struct media_transport *transport, + int8_t *volume); void media_transport_update_delay(struct media_transport *transport, uint16_t delay); void media_transport_update_volume(struct media_transport *transport, diff -Nru bluez-5.71/src/adapter.c bluez-5.72/src/adapter.c --- bluez-5.71/src/adapter.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/src/adapter.c 2024-01-13 06:25:53.000000000 +0800 @@ -311,6 +311,7 @@ bool pincode_requested; /* PIN requested during last bonding */ GSList *connections; /* Connected devices */ GSList *devices; /* Devices structure pointers */ + GSList *load_keys; /* Devices keys to be loaded */ GSList *connect_list; /* Devices to connect when found */ struct btd_device *connect_le; /* LE device waiting to be connected */ sdp_list_t *services; /* Services associated to adapter */ @@ -4284,6 +4285,9 @@ return -1; } +static void load_link_keys(struct btd_adapter *adapter, bool debug_keys, + bool retry); + static void load_link_keys_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -4293,18 +4297,31 @@ btd_error(adapter->dev_id, "Failed to load link keys for hci%u: %s (0x%02x)", adapter->dev_id, mgmt_errstr(status), status); + + if (status == MGMT_STATUS_INVALID_PARAMS) { + load_link_keys(adapter, btd_opts.debug_keys, true); + /* Release keys after retry since we shall only retry + * once. + */ + goto done; + } + return; } DBG("link keys loaded for hci%u", adapter->dev_id); + +done: + g_slist_free_full(adapter->load_keys, g_free); + adapter->load_keys = NULL; } -static void load_link_keys(struct btd_adapter *adapter, GSList *keys, - bool debug_keys) +static void load_link_keys(struct btd_adapter *adapter, bool debug_keys, + bool retry) { struct mgmt_cp_load_link_keys *cp; struct mgmt_link_key_info *key; - size_t key_count, cp_size; + size_t count, cp_size; unsigned int id; GSList *l; @@ -4318,12 +4335,14 @@ if (!(adapter->supported_settings & MGMT_SETTING_BREDR)) return; - key_count = g_slist_length(keys); + count = g_slist_length(adapter->load_keys); + if (!count) + return; - DBG("hci%u keys %zu debug_keys %d", adapter->dev_id, key_count, - debug_keys); + DBG("hci%u keys %zu debug_keys %d retry %s", adapter->dev_id, count, + debug_keys, retry ? "true" : "false"); - cp_size = sizeof(*cp) + (key_count * sizeof(*key)); + cp_size = sizeof(*cp) + (count * sizeof(*key)); cp = g_try_malloc0(cp_size); if (cp == NULL) { @@ -4341,13 +4360,18 @@ * behavior for debug keys. */ cp->debug_keys = debug_keys; - cp->key_count = htobs(key_count); + cp->key_count = htobs(count); - for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) { + for (l = adapter->load_keys, key = cp->keys; l != NULL; + l = g_slist_next(l), key++) { struct link_key_info *info = l->data; bacpy(&key->addr.bdaddr, &info->bdaddr); - key->addr.type = info->bdaddr_type; + /* Old kernels might only support loading with type set to + * BDADDR_BREDR so on retry set that instead of using the stored + * info. + */ + key->addr.type = retry ? BDADDR_BREDR : info->bdaddr_type; key->type = info->type; memcpy(key->val, info->key, 16); key->pin_len = info->pin_len; @@ -4359,9 +4383,12 @@ g_free(cp); - if (id == 0) + if (id == 0) { btd_error(adapter->dev_id, "Failed to load link keys for hci%u", adapter->dev_id); + g_slist_free_full(adapter->load_keys, g_free); + adapter->load_keys = NULL; + } } static void load_ltks_complete(uint8_t status, uint16_t length, @@ -4873,7 +4900,6 @@ static void load_devices(struct btd_adapter *adapter) { char dirname[PATH_MAX]; - GSList *keys = NULL; GSList *ltks = NULL; GSList *irks = NULL; GSList *params = NULL; @@ -4964,7 +4990,8 @@ } if (key_info) - keys = g_slist_append(keys, key_info); + adapter->load_keys = g_slist_append(adapter->load_keys, + key_info); if (ltk_info) ltks = g_slist_append(ltks, ltk_info); @@ -5013,8 +5040,7 @@ closedir(dir); - load_link_keys(adapter, keys, btd_opts.debug_keys); - g_slist_free_full(keys, g_free); + load_link_keys(adapter, btd_opts.debug_keys, false); load_ltks(adapter, ltks); g_slist_free_full(ltks, g_free); @@ -6930,6 +6956,9 @@ g_slist_free(adapter->devices); adapter->devices = NULL; + g_slist_free(adapter->load_keys); + adapter->load_keys = NULL; + discovery_cleanup(adapter, 0); unload_drivers(adapter); diff -Nru bluez-5.71/src/shared/bap.c bluez-5.72/src/shared/bap.c --- bluez-5.71/src/shared/bap.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/src/shared/bap.c 2024-01-13 06:25:53.000000000 +0800 @@ -177,6 +177,11 @@ void *user_data; }; +struct bt_bap_chan { + uint8_t count; + uint32_t location; +}; + struct bt_bap_pac { struct bt_bap_db *bdb; char *name; @@ -185,6 +190,7 @@ struct bt_bap_pac_qos qos; struct iovec *data; struct iovec *metadata; + struct queue *channels; struct bt_bap_pac_ops *ops; void *user_data; }; @@ -216,7 +222,7 @@ struct iovec *cc; struct iovec *meta; struct bt_bap_qos qos; - struct queue *links; + struct bt_bap_stream *link; struct bt_bap_stream_io *io; bool client; void *user_data; @@ -1004,14 +1010,6 @@ stream_io_free(io); } -static void bap_stream_unlink(void *data, void *user_data) -{ - struct bt_bap_stream *link = data; - struct bt_bap_stream *stream = user_data; - - queue_remove(link->links, stream); -} - static void bap_stream_free(void *data) { struct bt_bap_stream *stream = data; @@ -1019,8 +1017,9 @@ if (stream->ep) stream->ep->stream = NULL; - queue_foreach(stream->links, bap_stream_unlink, stream); - queue_destroy(stream->links, NULL); + if (stream->link) + stream->link->link = NULL; + stream_io_unref(stream->io); util_iov_free(stream->cc, 1); util_iov_free(stream->meta, 1); @@ -1044,12 +1043,12 @@ bap_stream_free(stream); } -static void bap_stream_io_link(void *data, void *user_data) +static bool bap_stream_io_link(const void *data, const void *user_data) { - struct bt_bap_stream *stream = data; - struct bt_bap_stream *link = user_data; + struct bt_bap_stream *stream = (void *)data; + struct bt_bap_stream *link = (void *)user_data; - bt_bap_stream_io_link(stream, link); + return !bt_bap_stream_io_link(stream, link); } static void bap_stream_update_io_links(struct bt_bap_stream *stream) @@ -1058,7 +1057,7 @@ DBG(bap, "stream %p", stream); - queue_foreach(bap->streams, bap_stream_io_link, stream); + queue_find(bap->streams, bap_stream_io_link, stream); } static struct bt_bap_stream_io *stream_io_ref(struct bt_bap_stream_io *io) @@ -1089,31 +1088,18 @@ return stream_io_ref(sio); } -static void stream_find_io(void *data, void *user_data) -{ - struct bt_bap_stream *stream = data; - struct bt_bap_stream_io **io = user_data; - - if (*io) - return; - - *io = stream->io; -} - static struct bt_bap_stream_io *stream_get_io(struct bt_bap_stream *stream) { - struct bt_bap_stream_io *io; - if (!stream) return NULL; if (stream->io) return stream->io; - io = NULL; - queue_foreach(stream->links, stream_find_io, &io); + if (stream->link) + return stream->link->io; - return io; + return NULL; } static bool stream_io_disconnected(struct io *io, void *user_data); @@ -1151,17 +1137,6 @@ return true; } -static bool match_stream_io(const void *data, const void *user_data) -{ - const struct bt_bap_stream *stream = data; - const struct bt_bap_stream_io *io = user_data; - - if (!stream->io) - return false; - - return stream->io == io; -} - static bool bap_stream_io_detach(struct bt_bap_stream *stream) { struct bt_bap_stream *link; @@ -1175,7 +1150,7 @@ io = stream->io; stream->io = NULL; - link = queue_find(stream->links, match_stream_io, io); + link = stream->link; if (link) { /* Detach link if in QoS state */ if (link->ep->state == BT_ASCS_ASE_STATE_QOS) @@ -2417,6 +2392,36 @@ return iov_append(data, cont->iov_len, cont->iov_base); } +static void bap_pac_foreach_channel(size_t i, uint8_t l, uint8_t t, uint8_t *v, + void *user_data) +{ + struct bt_bap_pac *pac = user_data; + struct bt_bap_chan *chan; + + if (!v) + return; + + if (!pac->channels) + pac->channels = queue_new(); + + chan = new0(struct bt_bap_chan, 1); + chan->count = *v; + chan->location = bt_bap_pac_get_locations(pac) ? : pac->qos.location; + + queue_push_tail(pac->channels, chan); +} + +static void bap_pac_update_channels(struct bt_bap_pac *pac, struct iovec *data) +{ + uint8_t type = 0x03; + + if (!data) + return; + + util_ltv_foreach(data->iov_base, data->iov_len, &type, + bap_pac_foreach_channel, pac); +} + static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data, struct iovec *metadata) { @@ -2426,6 +2431,9 @@ else pac->data = util_iov_dup(data, 1); + /* Update channels */ + bap_pac_update_channels(pac, data); + /* Merge metadata into existing record */ if (pac->metadata) ltv_merge(pac->metadata, metadata); @@ -2446,15 +2454,15 @@ pac->bdb = bdb; pac->name = name ? strdup(name) : NULL; pac->type = type; + if (codec) pac->codec = *codec; - if (data) - pac->data = util_iov_dup(data, 1); - if (metadata) - pac->metadata = util_iov_dup(metadata, 1); + if (qos) pac->qos = *qos; + bap_pac_merge(pac, data, metadata); + return pac; } @@ -2465,6 +2473,7 @@ free(pac->name); util_iov_free(pac->metadata, 1); util_iov_free(pac->data, 1); + queue_destroy(pac->channels, free); free(pac); } @@ -4505,7 +4514,16 @@ if (ep->stream->lpac != match->lpac) return false; - return ep->stream->rpac == match->rpac; + if (ep->stream->rpac != match->rpac) + return false; + + switch (ep->state) { + case BT_BAP_STREAM_STATE_CONFIG: + case BT_BAP_STREAM_STATE_QOS: + return true; + } + + return false; } static struct bt_bap_req *bap_req_new(struct bt_bap_stream *stream, @@ -4626,16 +4644,70 @@ } int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, - bt_bap_pac_select_t func, void *user_data) + int *count, bt_bap_pac_select_t func, + void *user_data) { + const struct queue_entry *lchan, *rchan; + if (!lpac || !rpac || !func) return -EINVAL; if (!lpac->ops || !lpac->ops->select) return -EOPNOTSUPP; - lpac->ops->select(lpac, rpac, &rpac->qos, - func, user_data, lpac->user_data); + for (lchan = queue_get_entries(lpac->channels); lchan; + lchan = lchan->next) { + struct bt_bap_chan *lc = lchan->data; + struct bt_bap_chan map = *lc; + int i; + + for (i = 0, rchan = queue_get_entries(rpac->channels); rchan; + rchan = rchan->next, i++) { + struct bt_bap_chan *rc = rchan->data; + + /* Try matching the channel count */ + if (!(map.count & rc->count)) + break; + + /* Check if location was set otherwise attempt to + * assign one based on the number of channels it + * supports. + */ + if (!rc->location) { + rc->location = bt_bap_pac_get_locations(rpac); + /* If channel count is 1 use a single + * location + */ + if (rc->count == 0x01) + rc->location &= BIT(i); + } + + /* Try matching the channel location */ + if (!(map.location & rc->location)) + break; + + lpac->ops->select(lpac, rpac, map.location & + rc->location, &rpac->qos, + func, user_data, + lpac->user_data); + if (count) + (*count)++; + + /* Check if there are any channels left to select */ + map.count &= ~(map.count & rc->count); + if (!map.count) + break; + + /* Check if there are any locations left to select */ + map.location &= ~(map.location & rc->location); + if (!map.location) + break; + + /* Check if device require AC*(i) settings */ + if (rc->count == 0x01) + map.count = map.count >> 1; + } + } return 0; } @@ -4843,14 +4915,6 @@ return req->id; } -static void bap_stream_enable_link(void *data, void *user_data) -{ - struct bt_bap_stream *stream = data; - struct iovec *metadata = user_data; - - bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata, NULL, NULL); -} - unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream, bool enable_links, struct iovec *metadata, @@ -4872,7 +4936,9 @@ if (!ret || !enable_links) return ret; - queue_foreach(stream->links, bap_stream_enable_link, metadata); + if (stream->link) + bap_stream_metadata(stream->link, BT_ASCS_ENABLE, + metadata, NULL, NULL); break; case BT_BAP_STREAM_TYPE_BCAST: if (!bt_bap_stream_io_dir(stream)) @@ -4935,26 +5001,6 @@ return 0; } -static void bap_stream_disable_link(void *data, void *user_data) -{ - struct bt_bap_stream *stream = data; - struct bt_bap_req *req; - struct iovec iov; - struct bt_ascs_disable disable; - - memset(&disable, 0, sizeof(disable)); - - disable.ase = stream->ep->id; - - iov.iov_base = &disable; - iov.iov_len = sizeof(disable); - - req = bap_req_new(stream, BT_ASCS_DISABLE, &iov, 1, NULL, NULL); - - if (!bap_queue_req(stream->bap, req)) - bap_req_free(req); -} - unsigned int bt_bap_stream_disable(struct bt_bap_stream *stream, bool disable_links, bt_bap_stream_func_t func, @@ -4990,8 +5036,7 @@ } if (disable_links) - queue_foreach(stream->links, bap_stream_disable_link, - NULL); + bt_bap_stream_disable(stream->link, false, NULL, NULL); return req->id; @@ -5198,7 +5243,8 @@ bap_stream_set_io(stream, INT_TO_PTR(fd)); - queue_foreach(stream->links, bap_stream_set_io, INT_TO_PTR(fd)); + if (stream->link) + bap_stream_set_io(stream, INT_TO_PTR(fd)); return true; } @@ -5251,22 +5297,17 @@ if (!stream || !link || stream == link) return -EINVAL; - if (queue_find(stream->links, NULL, link)) + if (stream->link || link->link) return -EALREADY; if (stream->client != link->client || stream->qos.ucast.cig_id != link->qos.ucast.cig_id || - stream->qos.ucast.cis_id != link->qos.ucast.cis_id) + stream->qos.ucast.cis_id != link->qos.ucast.cis_id || + stream->ep->dir == link->ep->dir) return -EINVAL; - if (!stream->links) - stream->links = queue_new(); - - if (!link->links) - link->links = queue_new(); - - queue_push_tail(stream->links, link); - queue_push_tail(link->links, stream); + stream->link = link; + link->link = stream; /* Link IOs if already set on stream/link */ if (stream->io && !link->io) @@ -5279,12 +5320,12 @@ return 0; } -struct queue *bt_bap_stream_io_get_links(struct bt_bap_stream *stream) +struct bt_bap_stream *bt_bap_stream_io_get_link(struct bt_bap_stream *stream) { if (!stream) return NULL; - return stream->links; + return stream->link; } static void bap_stream_get_in_qos(void *data, void *user_data) @@ -5292,6 +5333,9 @@ struct bt_bap_stream *stream = data; struct bt_bap_qos **qos = user_data; + if (!stream) + return; + if (!qos || *qos || stream->ep->dir != BT_BAP_SOURCE || !stream->qos.ucast.io_qos.sdu) return; @@ -5304,6 +5348,9 @@ struct bt_bap_stream *stream = data; struct bt_bap_qos **qos = user_data; + if (!stream) + return; + if (!qos || *qos || stream->ep->dir != BT_BAP_SINK || !stream->qos.ucast.io_qos.sdu) return; @@ -5321,11 +5368,11 @@ switch (stream->ep->dir) { case BT_BAP_SOURCE: bap_stream_get_in_qos(stream, in); - queue_foreach(stream->links, bap_stream_get_out_qos, out); + bap_stream_get_out_qos(stream->link, out); break; case BT_BAP_SINK: bap_stream_get_out_qos(stream, out); - queue_foreach(stream->links, bap_stream_get_in_qos, in); + bap_stream_get_in_qos(stream->link, in); break; default: return false; @@ -5336,14 +5383,6 @@ return in && out; } -static void bap_stream_get_dir(void *data, void *user_data) -{ - struct bt_bap_stream *stream = data; - uint8_t *dir = user_data; - - *dir |= stream->ep->dir; -} - uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream) { uint8_t dir; @@ -5353,7 +5392,8 @@ dir = stream->ep->dir; - queue_foreach(stream->links, bap_stream_get_dir, &dir); + if (stream->link) + dir |= stream->link->ep->dir; return dir; } @@ -5364,6 +5404,9 @@ int fd = PTR_TO_INT(user_data); const struct queue_entry *entry; + if (!stream) + return; + if (fd >= 0) bap_stream_io_attach(stream, fd, true); else @@ -5385,8 +5428,7 @@ return -EINVAL; bap_stream_io_connecting(stream, INT_TO_PTR(fd)); - - queue_foreach(stream->links, bap_stream_io_connecting, INT_TO_PTR(fd)); + bap_stream_io_connecting(stream->link, INT_TO_PTR(fd)); return 0; } diff -Nru bluez-5.71/src/shared/bap.h bluez-5.72/src/shared/bap.h --- bluez-5.71/src/shared/bap.h 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/src/shared/bap.h 2024-01-13 06:25:53.000000000 +0800 @@ -151,7 +151,7 @@ struct bt_bap_pac_ops { int (*select)(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, - struct bt_bap_pac_qos *qos, + uint32_t chan_alloc, struct bt_bap_pac_qos *qos, bt_bap_pac_select_t cb, void *cb_data, void *user_data); int (*config)(struct bt_bap_stream *stream, struct iovec *cfg, struct bt_bap_qos *qos, bt_bap_pac_config_t cb, @@ -234,7 +234,8 @@ /* Stream related functions */ int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, - bt_bap_pac_select_t func, void *user_data); + int *count, bt_bap_pac_select_t func, + void *user_data); struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap, struct bt_bap_pac *lpac, @@ -302,7 +303,7 @@ int bt_bap_stream_io_link(struct bt_bap_stream *stream, struct bt_bap_stream *link); -struct queue *bt_bap_stream_io_get_links(struct bt_bap_stream *stream); +struct bt_bap_stream *bt_bap_stream_io_get_link(struct bt_bap_stream *stream); bool bt_bap_stream_io_get_qos(struct bt_bap_stream *stream, struct bt_bap_qos **in, struct bt_bap_qos **out); diff -Nru bluez-5.71/src/shared/gatt-db.c bluez-5.72/src/shared/gatt-db.c --- bluez-5.71/src/shared/gatt-db.c 2023-06-30 16:10:20.000000000 +0800 +++ bluez-5.72/src/shared/gatt-db.c 2024-01-13 06:25:53.000000000 +0800 @@ -58,7 +58,7 @@ struct bt_crypto *crypto; uint8_t hash[16]; unsigned int hash_id; - uint16_t next_handle; + uint16_t last_handle; struct queue *services; struct queue *notify_list; @@ -255,7 +255,7 @@ db->crypto = bt_crypto_new(); db->services = queue_new(); db->notify_list = queue_new(); - db->next_handle = 0x0001; + db->last_handle = 0x0000; return gatt_db_ref(db); } @@ -356,14 +356,15 @@ db->hash_id = 0; - if (!db->next_handle) + if (gatt_db_isempty(db)) return false; - hash.iov = new0(struct iovec, db->next_handle); + hash.iov = new0(struct iovec, db->last_handle + 1); hash.i = 0; gatt_db_foreach_service(db, NULL, service_gen_hash_m, &hash); - bt_crypto_gatt_hash(db->crypto, hash.iov, db->next_handle, db->hash); + bt_crypto_gatt_hash(db->crypto, hash.iov, db->last_handle + 1, + db->hash); for (i = 0; i < hash.i; i++) free(hash.iov[i].iov_base); @@ -624,7 +625,7 @@ done: if (gatt_db_isempty(db)) - db->next_handle = 0; + db->last_handle = 0; return true; } @@ -700,7 +701,7 @@ return NULL; if (!handle) - handle = db->next_handle; + handle = db->last_handle + 1; if (num_handles < 1 || (handle + num_handles - 1) > UINT16_MAX) return NULL; @@ -747,8 +748,8 @@ service->attributes[0]->handle = handle; service->num_handles = num_handles; - /* Fast-forward next_handle if the new service was added to the end */ - db->next_handle = MAX(handle + num_handles, db->next_handle); + /* Fast-forward last_handle if the new service was added to the end */ + db->last_handle = MAX(handle + num_handles - 1, db->last_handle); return service->attributes[0]; diff -Nru bluez-5.71/src/shared/util.c bluez-5.72/src/shared/util.c --- bluez-5.71/src/shared/util.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/src/shared/util.c 2024-01-13 06:25:53.000000000 +0800 @@ -175,6 +175,49 @@ return NULL; } +/* Helper to itertate over LTV entries */ +bool util_ltv_foreach(const uint8_t *data, uint8_t len, uint8_t *type, + util_ltv_func_t func, void *user_data) +{ + struct iovec iov; + int i; + + if (!func) + return false; + + iov.iov_base = (void *) data; + iov.iov_len = len; + + for (i = 0; iov.iov_len; i++) { + uint8_t l, t, *v; + + if (!util_iov_pull_u8(&iov, &l)) + return false; + + if (!l) { + func(i, l, 0, NULL, user_data); + continue; + } + + if (!util_iov_pull_u8(&iov, &t)) + return false; + + l--; + + if (l) { + v = util_iov_pull_mem(&iov, l); + if (!v) + return false; + } else + v = NULL; + + if (!type || *type == t) + func(i, l, t, v, user_data); + } + + return true; +} + /* Helper to print debug information of LTV entries */ bool util_debug_ltv(const uint8_t *data, uint8_t len, const struct util_ltv_debugger *debugger, size_t num, diff -Nru bluez-5.71/src/shared/util.h bluez-5.72/src/shared/util.h --- bluez-5.71/src/shared/util.h 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/src/shared/util.h 2024-01-13 06:25:53.000000000 +0800 @@ -138,6 +138,12 @@ const struct util_ltv_debugger *debugger, size_t num, util_debug_func_t function, void *user_data); +typedef void (*util_ltv_func_t)(size_t i, uint8_t l, uint8_t t, uint8_t *v, + void *user_data); + +bool util_ltv_foreach(const uint8_t *data, uint8_t len, uint8_t *type, + util_ltv_func_t func, void *user_data); + unsigned char util_get_dt(const char *parent, const char *name); ssize_t util_getrandom(void *buf, size_t buflen, unsigned int flags); diff -Nru bluez-5.71/src/shared/vcp.c bluez-5.72/src/shared/vcp.c --- bluez-5.71/src/shared/vcp.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/src/shared/vcp.c 2024-01-13 06:25:53.000000000 +0800 @@ -39,6 +39,9 @@ #define BT_ATT_ERROR_INVALID_CHANGE_COUNTER 0x80 #define BT_ATT_ERROR_OPCODE_NOT_SUPPORTED 0x81 #define BT_ATT_ERROR_VALUE_OUT_OF_RANGE 0x82 +#define BT_ATT_AICS_ERROR_VALUE_OUT_OF_RANGE 0x83 +#define BT_ATT_AICS_ERROR_MUTE_DISABLED 0x82 +#define BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED 0x84 #define BT_VCP_NA BIT(0) #define BT_VCP_FRONT_LEFT BIT(1) @@ -70,10 +73,52 @@ #define BT_VCP_LEFT_SURROUND BIT(27) #define BT_VCP_RIGHT_SURROUND BIT(28) +#define VCS_TOTAL_NUM_HANDLES 11 +#define AICS_TOTAL_NUM_HANDLES 16 + +/* AICS Audio Input Type Values */ +#define AICS_AUD_IP_TYPE_UNSPECIFIED 0x00 +#define AICS_AUD_IP_TYPE_BLUETOOTH 0x01 +#define AICS_AUD_IP_TYPE_MICROPHONE 0x02 +#define AICS_AUD_IP_TYPE_ANALOG 0x03 +#define AICS_AUD_IP_TYPE_DIGITAL 0x04 +#define AICS_AUD_IP_TYPE_RADIO 0x05 +#define AICS_AUD_IP_TYPE_STREAMING 0x06 +#define AICS_AUD_IP_TYPE_AMBIENT 0x07 + +/* AICS Audio Input Status Values */ +#define AICS_AUD_IP_STATUS_INACTIVE 0x00 +#define AICS_AUD_IP_STATUS_ACTIVE 0x01 + +/* AICS Audio Input Control Point Opcodes */ +#define BT_AICS_SET_GAIN_SETTING 0x01 +#define BT_AICS_UNMUTE 0x02 +#define BT_AICS_MUTE 0x03 +#define BT_AICS_SET_MANUAL_GAIN_MODE 0x04 +#define BT_AICS_SET_AUTO_GAIN_MODE 0x05 + +/* AICS Gain Mode Field Value */ +#define AICS_GAIN_MODE_MANUAL_ONLY 0x00 +#define AICS_GAIN_MODE_AUTO_ONLY 0x01 +#define AICS_GAIN_MODE_MANUAL 0x02 +#define AICS_GAIN_MODE_AUTO 0x03 + +/* AICS Mute Field Values */ +#define AICS_NOT_MUTED 0x00 +#define AICS_MUTED 0x01 +#define AICS_DISABLED 0x02 + +#define AICS_GAIN_SETTING_UNITS 1 +#define AICS_GAIN_SETTING_MAX_VALUE 127 +#define AICS_GAIN_SETTING_MIN_VALUE -128 + +#define AICS_GAIN_SETTING_DEFAULT_VALUE 88 + struct bt_vcp_db { struct gatt_db *db; struct bt_vcs *vcs; struct bt_vocs *vocs; + struct bt_aics *aics; }; typedef void (*vcp_func_t)(struct bt_vcp *vcp, bool success, uint8_t att_ecode, @@ -138,6 +183,10 @@ unsigned int audio_loc_id; unsigned int ao_dec_id; + unsigned int aics_ip_state_id; + unsigned int aics_ip_status_id; + unsigned int aics_ip_descr_id; + struct queue *notify; struct queue *pending; @@ -190,6 +239,43 @@ struct gatt_db_attribute *voaodec_ccc; }; +struct aud_ip_st { + int8_t gain_setting; + uint8_t mute; + uint8_t gain_mode; + uint8_t chg_counter; +} __packed; + +struct gain_setting_prop { + uint8_t gain_setting_units; + int8_t gain_setting_min; + int8_t gain_setting_max; +} __packed; + +struct bt_aics_set_gain_setting { + uint8_t change_counter; + int8_t gain_setting; +} __packed; + +struct bt_aics { + struct bt_vcp_db *vdb; + struct aud_ip_st *aud_ipst; + struct gain_setting_prop *gain_settingprop; + uint8_t aud_input_type; + uint8_t aud_input_status; + char *aud_input_descr; + struct gatt_db_attribute *service; + struct gatt_db_attribute *aud_ip_state; + struct gatt_db_attribute *aud_ip_state_ccc; + struct gatt_db_attribute *gain_stting_prop; + struct gatt_db_attribute *aud_ip_type; + struct gatt_db_attribute *aud_ip_status; + struct gatt_db_attribute *aud_ip_status_ccc; + struct gatt_db_attribute *aud_ip_cp; + struct gatt_db_attribute *aud_ip_dscrptn; + struct gatt_db_attribute *aud_ip_dscrptn_ccc; +}; + static struct queue *vcp_db; static struct queue *vcp_cbs; static struct queue *sessions; @@ -268,6 +354,20 @@ return vcp->rdb->vocs; } +static struct bt_aics *vcp_get_aics(struct bt_vcp *vcp) +{ + if (!vcp) + return NULL; + + if (vcp->rdb->aics) + return vcp->rdb->aics; + + vcp->rdb->aics = new0(struct bt_aics, 1); + vcp->rdb->aics->vdb = vcp->rdb; + + return vcp->rdb->aics; +} + static void vcp_detached(void *data, void *user_data) { struct bt_vcp_cb *cb = data; @@ -298,6 +398,7 @@ free(vdb->vcs); free(vdb->vocs); + free(vdb->aics); free(vdb); } @@ -986,6 +1087,488 @@ iov.iov_len); } +static void aics_input_state_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_aics *aics = user_data; + struct iovec iov; + + iov.iov_base = aics->aud_ipst; + iov.iov_len = sizeof(*aics->aud_ipst); + + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, + iov.iov_len); +} + +static void aics_gain_setting_prop_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_aics *aics = user_data; + struct iovec iov; + + iov.iov_base = aics->gain_settingprop; + iov.iov_len = sizeof(*aics->gain_settingprop); + + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, + iov.iov_len); +} + +static void aics_audio_input_type_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_aics *aics = user_data; + struct iovec iov; + + iov.iov_base = &aics->aud_input_type; + iov.iov_len = sizeof(aics->aud_input_type); + + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, + iov.iov_len); +} + +static void aics_input_status_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_aics *aics = user_data; + struct iovec iov; + + iov.iov_base = &aics->aud_input_status; + iov.iov_len = sizeof(aics->aud_input_status); + + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, + iov.iov_len); +} + +static struct aud_ip_st *vdb_get_audipst(struct bt_vcp_db *vdb) +{ + if (!vdb->aics) + return NULL; + + if (vdb->aics->aud_ipst) + return vdb->aics->aud_ipst; + + return NULL; +} + +static struct gain_setting_prop *vdb_get_gainsettingprop( + struct bt_vcp_db *vdb) +{ + if (!vdb->aics) + return NULL; + + if (vdb->aics->gain_settingprop) + return vdb->aics->gain_settingprop; + + return NULL; +} + +static uint8_t aics_set_gain_setting(struct bt_aics *aics, + struct bt_vcp *vcp, struct iovec *iov) +{ + struct bt_vcp_db *vdb; + struct aud_ip_st *audipst; + struct bt_aics_set_gain_setting *req; + struct gain_setting_prop *gainsettngprop; + uint8_t ret = 1; + + vdb = vcp_get_vdb(vcp); + if (!vdb) { + DBG(vcp, "error: VDB not available"); + ret = 0; + goto respond; + } + + audipst = vdb_get_audipst(vdb); + if (!audipst) { + DBG(vcp, "error: Audio Input State value is not available"); + ret = 0; + goto respond; + + } + + req = iov_pull_mem(iov, sizeof(*req)); + if (!req) { + ret = 0; + goto respond; + + } + + if (req->change_counter != audipst->chg_counter) { + DBG(vcp, "Change Counter Mismatch Audio Input State!"); + ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; + goto respond; + } + + if (audipst->gain_mode != AICS_GAIN_MODE_MANUAL_ONLY && + audipst->gain_mode != AICS_GAIN_MODE_MANUAL) { + DBG(vcp, "Gain Mode is not Manual only or Manual"); + ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; + goto respond; + } + + gainsettngprop = vdb_get_gainsettingprop(vdb); + if (req->gain_setting > gainsettngprop->gain_setting_max || + req->gain_setting < gainsettngprop->gain_setting_min) { + DBG(vcp, "error: Value Out of Range"); + ret = BT_ATT_AICS_ERROR_VALUE_OUT_OF_RANGE; + goto respond; + } + + audipst->gain_setting = req->gain_setting; + /*Increment Change Counter*/ + audipst->chg_counter = -~audipst->chg_counter; + gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, + sizeof(struct aud_ip_st), + bt_vcp_get_att(vcp)); + ret = 0; + +respond: + return ret; +} + +static uint8_t aics_unmute(struct bt_aics *aics, struct bt_vcp *vcp, + struct iovec *iov) +{ + struct bt_vcp_db *vdb; + struct aud_ip_st *audipst; + uint8_t *change_counter; + uint8_t ret = 1; + + vdb = vcp_get_vdb(vcp); + if (!vdb) { + DBG(vcp, "error: VDB not available"); + ret = 0; + goto respond; + + } + + audipst = vdb_get_audipst(vdb); + if (!audipst) { + DBG(vcp, "error: Audio Input State value is not available"); + ret = 0; + goto respond; + + } + change_counter = iov_pull_mem(iov, sizeof(*change_counter)); + if (!change_counter) { + ret = 0; + goto respond; + + } + + if (*change_counter != audipst->chg_counter) { + DBG(vcp, "Change Counter Mismatch Audio Input State!"); + ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; + goto respond; + } + + if (audipst->mute == AICS_DISABLED) { + DBG(vcp, "Mute state is Disabled!"); + ret = BT_ATT_AICS_ERROR_MUTE_DISABLED; + goto respond; + } + + audipst->mute = AICS_NOT_MUTED; + /*Increment Change Counter*/ + audipst->chg_counter = -~audipst->chg_counter; + gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, + sizeof(struct aud_ip_st), + bt_vcp_get_att(vcp)); + ret = 0; + +respond: + return ret; +} + +static uint8_t aics_mute(struct bt_aics *aics, struct bt_vcp *vcp, + struct iovec *iov) +{ + struct bt_vcp_db *vdb; + struct aud_ip_st *audipst; + uint8_t *change_counter; + uint8_t ret = 1; + + vdb = vcp_get_vdb(vcp); + if (!vdb) { + DBG(vcp, "error: VDB not available"); + ret = 0; + goto respond; + } + + audipst = vdb_get_audipst(vdb); + if (!audipst) { + DBG(vcp, "error: Audio Input State value is not available"); + ret = 0; + goto respond; + } + change_counter = iov_pull_mem(iov, sizeof(*change_counter)); + if (!change_counter) { + ret = 0; + goto respond; + } + + if (*change_counter != audipst->chg_counter) { + DBG(vcp, "Change Counter Mismatch Audio Input State!"); + ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; + goto respond; + } + + if (audipst->mute == AICS_DISABLED) { + DBG(vcp, "Mute state is Disabled!"); + ret = BT_ATT_AICS_ERROR_MUTE_DISABLED; + goto respond; + } + + audipst->mute = AICS_MUTED; + /*Increment Change Counter*/ + audipst->chg_counter = -~audipst->chg_counter; + gatt_db_attribute_notify(vdb->aics->aud_ip_state, (void *)audipst, + sizeof(struct aud_ip_st), + bt_vcp_get_att(vcp)); + ret = 0; + +respond: + return ret; +} + +static uint8_t aics_set_manual_gain_mode(struct bt_aics *aics, + struct bt_vcp *vcp, struct iovec *iov) +{ + struct bt_vcp_db *vdb; + struct aud_ip_st *audipst; + uint8_t *change_counter; + uint8_t ret = 1; + + vdb = vcp_get_vdb(vcp); + if (!vdb) { + DBG(vcp, "error: VDB not available"); + ret = 0; + goto respond; + } + + audipst = vdb_get_audipst(vdb); + if (!audipst) { + DBG(vcp, "error: Audio Input State value is not available"); + ret = 0; + goto respond; + } + + change_counter = iov_pull_mem(iov, sizeof(*change_counter)); + if (!change_counter) { + ret = 0; + goto respond; + } + + if (*change_counter != audipst->chg_counter) { + DBG(vcp, "Change Counter Mismatch Audio Input State!"); + ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; + goto respond; + } + + if (audipst->gain_mode == AICS_GAIN_MODE_AUTO_ONLY || + audipst->gain_mode == AICS_GAIN_MODE_MANUAL_ONLY) { + DBG(vcp, "error!! gain mode is Automatic only or Manual only"); + ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; + goto respond; + } + + if (audipst->gain_mode == AICS_GAIN_MODE_AUTO) { + audipst->gain_mode = AICS_GAIN_MODE_MANUAL; + /*Increment Change Counter*/ + audipst->chg_counter = -~audipst->chg_counter; + gatt_db_attribute_notify(vdb->aics->aud_ip_state, + (void *)audipst, + sizeof(struct aud_ip_st), + bt_vcp_get_att(vcp)); + ret = 0; + } else { + DBG(vcp, + "error!! Gain mode field value not Automatic"); + ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; + } + +respond: + return ret; +} + +static uint8_t aics_set_auto_gain_mode(struct bt_aics *aics, struct bt_vcp *vcp, + struct iovec *iov) +{ + struct bt_vcp_db *vdb; + struct aud_ip_st *audipst; + uint8_t *change_counter; + uint8_t ret = 1; + + vdb = vcp_get_vdb(vcp); + if (!vdb) { + DBG(vcp, "error: VDB not available"); + ret = 0; + goto respond; + } + + audipst = vdb_get_audipst(vdb); + if (!audipst) { + DBG(vcp, "error: Audio Input State value is not available"); + ret = 0; + goto respond; + } + change_counter = iov_pull_mem(iov, sizeof(*change_counter)); + if (!change_counter) { + ret = 0; + goto respond; + } + + if (*change_counter != audipst->chg_counter) { + DBG(vcp, "Change Counter Mismatch Audio Input State!"); + ret = BT_ATT_ERROR_INVALID_CHANGE_COUNTER; + goto respond; + } + + if (audipst->gain_mode == AICS_GAIN_MODE_AUTO_ONLY || + audipst->gain_mode == AICS_GAIN_MODE_MANUAL_ONLY) { + DBG(vcp, "error!! gain mode is Automatic only or Manual only"); + ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; + goto respond; + } + + if (audipst->gain_mode == AICS_GAIN_MODE_MANUAL) { + audipst->gain_mode = AICS_GAIN_MODE_AUTO; + /*Increment Change Counter*/ + audipst->chg_counter = -~audipst->chg_counter; + gatt_db_attribute_notify(vdb->aics->aud_ip_state, + (void *)audipst, + sizeof(struct aud_ip_st), bt_vcp_get_att(vcp)); + ret = 0; + } else { + DBG(vcp, "error!! Gain mode field value is not Manual"); + ret = BT_ATT_AICS_ERROR_GAIN_MODE_CHANGE_NOT_ALLOWED; + } + +respond: + return ret; +} + +#define AICS_OP(_str, _op, _size, _func) \ + { \ + .str = _str, \ + .op = _op, \ + .size = _size, \ + .func = _func, \ + } + +struct aics_op_handler { + const char *str; + uint8_t op; + size_t size; + uint8_t (*func)(struct bt_aics *aics, struct bt_vcp *vcp, + struct iovec *iov); +} aics_handlers[] = { + AICS_OP("Set Gain Setting", BT_AICS_SET_GAIN_SETTING, + sizeof(struct bt_aics_set_gain_setting), + aics_set_gain_setting), + AICS_OP("Unmute", BT_AICS_UNMUTE, + sizeof(uint8_t), aics_unmute), + AICS_OP("Mute", BT_AICS_MUTE, + sizeof(uint8_t), aics_mute), + AICS_OP("Set Manual Gain Mode", BT_AICS_SET_MANUAL_GAIN_MODE, + sizeof(uint8_t), aics_set_manual_gain_mode), + AICS_OP("Set Automatic Gain Mode", BT_AICS_SET_AUTO_GAIN_MODE, + sizeof(uint8_t), aics_set_auto_gain_mode), + {} +}; + +static void aics_ip_cp_write(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_aics *aics = user_data; + struct bt_vcp *vcp = vcp_get_session(att, aics->vdb->db); + struct iovec iov = { + .iov_base = (void *) value, + .iov_len = len, + }; + uint8_t *aics_op; + struct aics_op_handler *handler; + uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; + + DBG(vcp, "AICS Control Point Write"); + + if (offset) { + DBG(vcp, "invalid offset %d", offset); + ret = BT_ATT_ERROR_INVALID_OFFSET; + goto respond; + } + + if (len < sizeof(*aics_op)) { + DBG(vcp, "invalid len %ld < %ld sizeof(*param)", len, + sizeof(*aics_op)); + ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; + goto respond; + } + + aics_op = iov_pull_mem(&iov, sizeof(*aics_op)); + + for (handler = aics_handlers; handler && handler->str; handler++) { + if (handler->op != *aics_op) + continue; + + if (iov.iov_len < handler->size) { + DBG(vcp, "invalid len %ld < %ld handler->size", len, + handler->size); + ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; + goto respond; + } + + break; + } + + if (handler && handler->str) { + DBG(vcp, "%s", handler->str); + + ret = handler->func(aics, vcp, &iov); + } else { + DBG(vcp, "Unknown opcode 0x%02x", *aics_op); + ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED; + } + +respond: + gatt_db_attribute_write_result(attrib, id, ret); +} + +static void aics_input_descr_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct bt_aics *aics = user_data; + struct iovec iov; + + iov.iov_base = aics->aud_input_descr; + iov.iov_len = strlen(aics->aud_input_descr); + + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base, + iov.iov_len); +} + +static void aics_input_descr_write(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + /* TODO : AICS optional feature */ +} + static struct bt_vcs *vcs_new(struct gatt_db *db, struct bt_vcp_db *vdb) { struct bt_vcs *vcs; @@ -1004,9 +1587,12 @@ /* Populate DB with VCS attributes */ bt_uuid16_create(&uuid, VCS_UUID); - vcs->service = gatt_db_add_service(db, &uuid, true, 10); + vcs->service = gatt_db_add_service(db, &uuid, true, + VCS_TOTAL_NUM_HANDLES); gatt_db_service_add_included(vcs->service, vdb->vocs->service); gatt_db_service_set_active(vdb->vocs->service, true); + gatt_db_service_add_included(vcs->service, vdb->aics->service); + gatt_db_service_set_active(vdb->aics->service, true); bt_uuid16_create(&uuid, VOL_STATE_CHRC_UUID); vcs->vs = gatt_db_service_add_characteristic(vcs->service, @@ -1115,6 +1701,108 @@ return vocs; } +static struct bt_aics *aics_new(struct gatt_db *db) +{ + struct bt_aics *aics; + struct aud_ip_st *aics_aud_ip_st; + struct gain_setting_prop *aics_gain_settng_prop; + char *ip_descr; + char ip_descr_str[] = "Blueooth"; + bt_uuid_t uuid; + + if (!db) + return NULL; + + aics = new0(struct bt_aics, 1); + + aics_aud_ip_st = new0(struct aud_ip_st, 1); + aics_gain_settng_prop = new0(struct gain_setting_prop, 1); + ip_descr = malloc(256); + memset(ip_descr, 0, 256); + + aics_aud_ip_st->mute = AICS_NOT_MUTED; + aics_aud_ip_st->gain_mode = AICS_GAIN_MODE_MANUAL; + aics_aud_ip_st->gain_setting = AICS_GAIN_SETTING_DEFAULT_VALUE; + aics->aud_ipst = aics_aud_ip_st; + aics_gain_settng_prop->gain_setting_units = AICS_GAIN_SETTING_UNITS; + aics_gain_settng_prop->gain_setting_max = AICS_GAIN_SETTING_MAX_VALUE; + aics_gain_settng_prop->gain_setting_min = AICS_GAIN_SETTING_MIN_VALUE; + aics->gain_settingprop = aics_gain_settng_prop; + aics->aud_input_type = AICS_AUD_IP_TYPE_BLUETOOTH; + aics->aud_input_status = AICS_AUD_IP_STATUS_ACTIVE; + memcpy(ip_descr, ip_descr_str, strlen(ip_descr_str)); + aics->aud_input_descr = ip_descr; + + /* Populate DB with AICS attributes */ + bt_uuid16_create(&uuid, AUDIO_INPUT_CS_UUID); + aics->service = gatt_db_add_service(db, &uuid, false, + AICS_TOTAL_NUM_HANDLES); + + bt_uuid16_create(&uuid, AICS_INPUT_STATE_CHAR_UUID); + aics->aud_ip_state = gatt_db_service_add_characteristic(aics->service, + &uuid, + BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_NOTIFY, + aics_input_state_read, + NULL, + aics); + aics->aud_ip_state_ccc = gatt_db_service_add_ccc(aics->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, AICS_GAIN_SETTING_PROP_CHAR_UUID); + aics->gain_stting_prop = gatt_db_service_add_characteristic( + aics->service, + &uuid, + BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + aics_gain_setting_prop_read, NULL, + aics); + + bt_uuid16_create(&uuid, AICS_AUDIO_INPUT_TYPE_CHAR_UUID); + aics->aud_ip_type = gatt_db_service_add_characteristic(aics->service, + &uuid, + BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + aics_audio_input_type_read, NULL, + aics); + + bt_uuid16_create(&uuid, AICS_INPUT_STATUS_CHAR_UUID); + aics->aud_ip_status = gatt_db_service_add_characteristic(aics->service, + &uuid, + BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_NOTIFY, + aics_input_status_read, NULL, + aics); + aics->aud_ip_status_ccc = gatt_db_service_add_ccc(aics->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + bt_uuid16_create(&uuid, AICS_AUDIO_INPUT_CP_CHRC_UUID); + aics->aud_ip_cp = gatt_db_service_add_characteristic(aics->service, + &uuid, + BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_WRITE, + NULL, aics_ip_cp_write, + aics); + + bt_uuid16_create(&uuid, AICS_INPUT_DESCR_CHAR_UUID); + aics->aud_ip_dscrptn = gatt_db_service_add_characteristic(aics->service, + &uuid, + BT_ATT_PERM_READ | + BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP | + BT_GATT_CHRC_PROP_NOTIFY, + aics_input_descr_read, + aics_input_descr_write, + aics); + aics->aud_ip_dscrptn_ccc = gatt_db_service_add_ccc(aics->service, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE); + + return aics; +} + static struct bt_vcp_db *vcp_db_new(struct gatt_db *db) { struct bt_vcp_db *vdb; @@ -1130,6 +1818,10 @@ vdb->vocs = vocs_new(db); vdb->vocs->vdb = vdb; + + vdb->aics = aics_new(db); + vdb->aics->vdb = vdb; + vdb->vcs = vcs_new(db, vdb); vdb->vcs->vdb = vdb; @@ -1689,6 +2381,307 @@ } +static void read_aics_audio_ip_state(struct bt_vcp *vcp, bool success, + uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct aud_ip_st *ip_st; + struct iovec iov = { + .iov_base = (void *) value, + .iov_len = length, + }; + + if (!success) { + DBG(vcp, "Unable to read Audio Input State: error 0x%02x", + att_ecode); + return; + } + + ip_st = iov_pull_mem(&iov, sizeof(*ip_st)); + if (!ip_st) { + DBG(vcp, "Unable to get Audio Input State"); + return; + } + + DBG(vcp, "Audio Input State, Gain Setting:%d", ip_st->gain_setting); + DBG(vcp, "Audio Input State, Mute:%x", ip_st->mute); + DBG(vcp, "Audio Input State, Gain Mode:%x", ip_st->gain_mode); + DBG(vcp, "Audio Input State, Change Counter:%x", ip_st->chg_counter); +} + +static void aics_ip_state_notify(struct bt_vcp *vcp, uint16_t value_handle, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct aud_ip_st ip_st; + + memcpy(&ip_st, value, sizeof(struct aud_ip_st)); + + DBG(vcp, "Audio Input State, Gain Setting:%d", ip_st.gain_setting); + DBG(vcp, "Audio Input State, Mute:%x", ip_st.mute); + DBG(vcp, "Audio Input State, Gain Mode:%x", ip_st.gain_mode); + DBG(vcp, "Audio Input State, Change Counter:%x", ip_st.chg_counter); +} + +static void read_aics_gain_setting_prop(struct bt_vcp *vcp, bool success, + uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct gain_setting_prop *aics_gain_setting_prop; + struct iovec iov = { + .iov_base = (void *) value, + .iov_len = length, + }; + + if (!value) { + DBG(vcp, "Unable to get Gain Setting Properties Char"); + return; + } + + if (!success) { + DBG(vcp, + "Unable to read Gain Setting Properties Char: 0x%02x", + att_ecode); + return; + } + + aics_gain_setting_prop = iov_pull_mem(&iov, + sizeof(*aics_gain_setting_prop)); + if (!aics_gain_setting_prop) { + DBG(vcp, "Unable to get Gain Setting Properties Char"); + return; + } + + DBG(vcp, "Gain Setting Properties, Units: %x", + aics_gain_setting_prop->gain_setting_units); + DBG(vcp, "Gain Setting Properties, Min Value: %d", + aics_gain_setting_prop->gain_setting_min); + DBG(vcp, "Gain Setting Properties, Max Value: %d", + aics_gain_setting_prop->gain_setting_max); +} + +static void read_aics_aud_ip_type(struct bt_vcp *vcp, bool success, + uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + uint8_t ip_type; + + if (!success) { + DBG(vcp, + "Unable to read Audio Input Type Char: error 0x%02x", + att_ecode); + return; + } + + memcpy(&ip_type, value, length); + + DBG(vcp, "Audio Input Type : %x", ip_type); +} + +static void read_aics_audio_ip_status(struct bt_vcp *vcp, bool success, + uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + uint8_t ip_status; + + if (!success) { + DBG(vcp, + "Unable to read Audio Input Status Char: 0x%02x", att_ecode); + return; + } + + memcpy(&ip_status, value, length); + + DBG(vcp, "Audio Input Status : %x", ip_status); +} + +static void aics_ip_status_notify(struct bt_vcp *vcp, uint16_t value_handle, + const uint8_t *value, + uint16_t length, + void *user_data) +{ + uint8_t ip_status; + + memcpy(&ip_status, value, length); + + DBG(vcp, "Audio Input Status, %x", ip_status); +} + +static void read_aics_audio_ip_description(struct bt_vcp *vcp, bool success, + uint8_t att_ecode, + const uint8_t *value, + uint16_t length, + void *user_data) +{ + char *ip_descrptn; + + if (!value) { + DBG(vcp, "Unable to get Audio Input Description"); + return; + } + + if (!success) { + DBG(vcp, + "Unable to read Audio Input Description Char: error 0x%02x", + att_ecode); + return; + } + + ip_descrptn = malloc(length+1); + memset(ip_descrptn, 0, length+1); + memcpy(ip_descrptn, value, length); + + if (!ip_descrptn) { + DBG(vcp, "Unable to get Audio Input Description"); + return; + } + + DBG(vcp, "Audio Input Description: %s", ip_descrptn); + free(ip_descrptn); + ip_descrptn = NULL; +} + +static void aics_audio_ip_desr_notify(struct bt_vcp *vcp, uint16_t value_handle, + const uint8_t *value, uint16_t length, + void *user_data) +{ + char *aud_ip_desr; + + aud_ip_desr = malloc(length+1); + memset(aud_ip_desr, 0, length+1); + memcpy(aud_ip_desr, value, length); + + DBG(vcp, "Audio Input Description Notify, %s", aud_ip_desr); + free(aud_ip_desr); + aud_ip_desr = NULL; +} + +static void foreach_aics_char(struct gatt_db_attribute *attr, void *user_data) +{ + struct bt_vcp *vcp = user_data; + uint16_t value_handle; + bt_uuid_t uuid, uuid_ipstate, uuid_gain_setting_prop, uuid_ip_type, + uuid_ip_status, uuid_ip_cp, uuid_ip_decs; + struct bt_aics *aics; + + if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, + NULL, NULL, &uuid)) + return; + + bt_uuid16_create(&uuid_ipstate, AICS_INPUT_STATE_CHAR_UUID); + bt_uuid16_create(&uuid_gain_setting_prop, + AICS_GAIN_SETTING_PROP_CHAR_UUID); + bt_uuid16_create(&uuid_ip_type, AICS_AUDIO_INPUT_TYPE_CHAR_UUID); + bt_uuid16_create(&uuid_ip_status, AICS_INPUT_STATUS_CHAR_UUID); + bt_uuid16_create(&uuid_ip_cp, AICS_AUDIO_INPUT_CP_CHRC_UUID); + bt_uuid16_create(&uuid_ip_decs, AICS_INPUT_DESCR_CHAR_UUID); + + + if (!bt_uuid_cmp(&uuid, &uuid_ipstate)) { + DBG(vcp, + "AICS Audio Input State Char found: handle 0x%04x", + value_handle); + + aics = vcp_get_aics(vcp); + if (!aics || aics->aud_ip_state) + return; + + aics->aud_ip_state = attr; + + vcp_read_value(vcp, value_handle, + read_aics_audio_ip_state, vcp); + + vcp->aics_ip_state_id = vcp_register_notify(vcp, value_handle, + aics_ip_state_notify, NULL); + + return; + } + + if (!bt_uuid_cmp(&uuid, &uuid_gain_setting_prop)) { + DBG(vcp, + "AICS Gain Setting Properties Char found: handle 0x%04x", + value_handle); + + aics = vcp_get_aics(vcp); + if (!aics || aics->gain_stting_prop) + return; + + aics->gain_stting_prop = attr; + + vcp_read_value(vcp, value_handle, read_aics_gain_setting_prop, + vcp); + return; + } + + if (!bt_uuid_cmp(&uuid, &uuid_ip_type)) { + DBG(vcp, "AICS Audio Input Type Char found: handle 0x%04x", + value_handle); + + aics = vcp_get_aics(vcp); + if (!aics || aics->gain_stting_prop) + return; + + aics->aud_ip_type = attr; + + vcp_read_value(vcp, value_handle, read_aics_aud_ip_type, + vcp); + return; + } + + if (!bt_uuid_cmp(&uuid, &uuid_ip_status)) { + DBG(vcp, + "AICS Audio Input Status Char found: handle 0x%04x", + value_handle); + + aics = vcp_get_aics(vcp); + if (!aics || aics->aud_ip_status) + return; + + aics->aud_ip_status = attr; + + vcp_read_value(vcp, value_handle, + read_aics_audio_ip_status, vcp); + + vcp->aics_ip_status_id = vcp_register_notify(vcp, value_handle, + aics_ip_status_notify, NULL); + + return; + } + + if (!bt_uuid_cmp(&uuid, &uuid_ip_cp)) { + DBG(vcp, "AICS Input CP found: handle 0x%04x", value_handle); + + aics = vcp_get_aics(vcp); + if (!aics || aics->aud_ip_cp) + return; + + aics->aud_ip_cp = attr; + + return; + } + + if (!bt_uuid_cmp(&uuid, &uuid_ip_decs)) { + DBG(vcp, + "AICS Audio Input Description Char found: handle 0x%04x", + value_handle); + + aics = vcp_get_aics(vcp); + if (!aics || aics->aud_ip_dscrptn) + return; + + aics->aud_ip_dscrptn = attr; + + vcp_read_value(vcp, value_handle, + read_aics_audio_ip_description, vcp); + vcp->aics_ip_descr_id = vcp_register_notify(vcp, value_handle, + aics_audio_ip_desr_notify, NULL); + } +} + static void foreach_vcs_service(struct gatt_db_attribute *attr, void *user_data) { @@ -1715,6 +2708,19 @@ gatt_db_service_foreach_char(attr, foreach_vocs_char, vcp); } +static void foreach_aics_service(struct gatt_db_attribute *attr, + void *user_data) +{ + struct bt_vcp *vcp = user_data; + struct bt_aics *aics = vcp_get_aics(vcp); + + aics->service = attr; + + gatt_db_service_set_claimed(attr, true); + + gatt_db_service_foreach_char(attr, foreach_aics_char, vcp); +} + bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client) { bt_uuid_t uuid; @@ -1740,6 +2746,9 @@ bt_uuid16_create(&uuid, VOL_OFFSET_CS_UUID); gatt_db_foreach_service(vcp->rdb->db, &uuid, foreach_vocs_service, vcp); + bt_uuid16_create(&uuid, AUDIO_INPUT_CS_UUID); + gatt_db_foreach_service(vcp->rdb->db, &uuid, foreach_aics_service, vcp); + return true; } diff -Nru bluez-5.71/unit/test-gatt.c bluez-5.72/unit/test-gatt.c --- bluez-5.71/unit/test-gatt.c 2022-03-16 23:06:20.000000000 +0800 +++ bluez-5.72/unit/test-gatt.c 2024-01-13 06:25:53.000000000 +0800 @@ -1908,6 +1908,60 @@ return make_db(specs); } +/* + * Tiny database which fits into a single minimum sized-pdu with services + * added in the following order to check ability to create hash db: + * - one secondary service at handle 0x0003, + * - one primary service at the max handle, + * - one primary service at handle 0x0001. + */ + +static struct gatt_db *make_test_tail_db(void) +{ + const struct att_handle_spec specs[] = { + SECONDARY_SERVICE(0x0003, DEVICE_INFORMATION_UUID, 16), + CHARACTERISTIC_STR(GATT_CHARAC_MANUFACTURER_NAME_STRING, + BT_ATT_PERM_READ | + BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_NOTIFY | + BT_GATT_CHRC_PROP_INDICATE | + BT_GATT_CHRC_PROP_EXT_PROP, + "BlueZ"), + DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID, BT_ATT_PERM_READ | + BT_ATT_PERM_WRITE, 0x00, 0x00), + DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ, + "Manufacturer Name"), + DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01, + 0x00), + CHARACTERISTIC_STR(GATT_CHARAC_SOFTWARE_REVISION_STRING, + BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_INDICATE, + "5.59"), + DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID, BT_ATT_PERM_READ + | BT_ATT_PERM_WRITE, 0x00, 0x00), + + PRIMARY_SERVICE(0xFFFF - 9 + 1, GAP_UUID, 9), + INCLUDE(0x0003), + CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + "BlueZ Unit Tester"), + CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef, + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_EXT_PROP, 0x09), + DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01, + 0x00), + CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, 0x00, 0x00), + PRIMARY_SERVICE(0x0001, DEVICE_INFORMATION_UUID, 1), + { } + }; + + return make_db(specs); +} + static void test_client(gconstpointer data) { create_context(512, data); @@ -2345,10 +2399,22 @@ .length = 0x03, }; +static void test_hash_db(gconstpointer data) +{ + struct context *context = create_context(512, data); + + /* test that gatt_db_get_hash is able to manage services at tail end of + * a db. + */ + gatt_db_get_hash(context->server_db); + + context_quit(context); +} + int main(int argc, char *argv[]) { struct gatt_db *service_db_1, *service_db_2, *service_db_3; - struct gatt_db *ts_small_db, *ts_large_db_1; + struct gatt_db *ts_small_db, *ts_large_db_1, *ts_tail_db; tester_init(&argc, &argv); @@ -2357,6 +2423,7 @@ service_db_3 = make_service_data_3_db(); ts_small_db = make_test_spec_small_db(); ts_large_db_1 = make_test_spec_large_db_1(); + ts_tail_db = make_test_tail_db(); /* * Server Configuration @@ -4487,5 +4554,9 @@ raw_pdu(0xff, 0x00), raw_pdu()); + define_test_server("/robustness/hash-db", + test_hash_db, ts_tail_db, NULL, + {}); + return tester_run(); } diff -Nru bluez-5.71/unit/test-vcp.c bluez-5.72/unit/test-vcp.c --- bluez-5.71/unit/test-vcp.c 2023-12-14 05:40:27.000000000 +0800 +++ bluez-5.72/unit/test-vcp.c 2024-01-13 06:25:53.000000000 +0800 @@ -206,7 +206,7 @@ * ATT: Exchange MTU Response (0x03) len 2 * Server RX MTU: 64 */ -#define VOCS_EXCHANGE_MTU \ +#define VCS_EXCHANGE_MTU \ IOV_DATA(0x02, 0x40, 0x00), \ IOV_DATA(0x03, 0x40, 0x00) @@ -217,23 +217,23 @@ * ATT: Read By Group Type Response (0x11) len 7 * Attribute data length: 6 * Attribute group list: 1 entry - * Handle range: 0x000d-0x0016 + * Handle range: 0x001d-0x0027 * UUID: Volume Control (0x1844) * * ATT: Read By Group Type Request (0x10) len 6 - * Handle range: 0x0017-0xffff + * Handle range: 0x0027-0xffff * Attribute group type: Primary Service (0x2800) * * ATT: Error Response (0x01) len 4 * Read By Group Type Request (0x10) - * Handle: 0x0017 + * Handle: 0x0027 * Error: Attribute Not Found (0x0a) */ -#define VOCS_PRIMARY_SERVICE_VCS \ +#define VOCS_AICS_PRIMARY_SERVICE_VCS \ IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ - IOV_DATA(0x11, 0x06, 0x0d, 0x00, 0x16, 0x00, 0x44, 0x18), \ - IOV_DATA(0x10, 0x17, 0x00, 0xff, 0xff, 0x00, 0x28), \ - IOV_DATA(0x01, 0x10, 0x17, 0x00, 0x0a) + IOV_DATA(0x11, 0x06, 0x1d, 0x00, 0x27, 0x00, 0x44, 0x18), \ + IOV_DATA(0x10, 0x27, 0x00, 0xff, 0xff, 0x00, 0x28), \ + IOV_DATA(0x01, 0x10, 0x27, 0x00, 0x0a) /* ATT: Read By Group Type Request (0x10) len 6 * Handle range: 0x0001-0xffff @@ -241,24 +241,28 @@ * * ATT: Read By Group Type Response (0x11) len 7 * Attribute data length: 6 - * Attribute group list: 1 entry + * Attribute group list: 2 entry * Handle range: 0x0001-0x000c * UUID: Volume Offset Control (0x1845) + * Handle range: 0x000d-0x001c + * UUID: Audio Input Control (0x1843) * * ATT: Read By Group Type Request (0x10) len 6 - * Handle range: 0x000d-0xffff + * Handle range: 0x001d-0xffff * Attribute group type: Secondary Service (0x2801) * * ATT: Error Response (0x01) len 4 * Read By Group Type Request (0x10) - * Handle: 0x000d + * Handle: 0x001d * Error: Attribute Not Found (0x0a) */ -#define VOCS_SECONDARY_SERVICE_VOCS \ +#define VOCS_AICS_SECONDARY_SERVICE \ IOV_DATA(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \ - IOV_DATA(0x11, 0x06, 0x01, 0x00, 0x0c, 0x00, 0x45, 0x18), \ - IOV_DATA(0x10, 0x0d, 0x00, 0xff, 0xff, 0x01, 0x28), \ - IOV_DATA(0x01, 0x10, 0x0d, 0x00, 0x0a) + IOV_DATA(0x11, 0x06, \ + 0x01, 0x00, 0x0c, 0x00, 0x45, 0x18, \ + 0x0d, 0x00, 0x1c, 0x00, 0x43, 0x18), \ + IOV_DATA(0x10, 0x1d, 0x00, 0xff, 0xff, 0x01, 0x28), \ + IOV_DATA(0x01, 0x10, 0x1d, 0x00, 0x0a) /* ATT: Read By Type Request (0x08) len 6 * Handle range: 0x0001-0xffff @@ -266,25 +270,28 @@ * * ATT: Read By Type Response (0x09) len 9 * Attribute data length: 8 - * Attribute data list: 1 entry - * Handle: 0x000e + * Attribute data list: 2 entries + * Handle: 0x001e * Value: 01000c004518 + * Handle: 0x001f + * Value: 0d001c004318 * * ATT: Read By Type Request (0x08) len 6 - * Handle range: 0x000f-0xffff + * Handle range: 0x0020-0xffff * Attribute type: Include (0x2802) * * ATT: Error Response (0x01) len 4 * Read By Type Request (0x08) - * Handle: 0x000f + * Handle: 0x0020 * Error: Attribute Not Found (0x0a) */ -#define VOCS_INCLUDED_SERVICE_VOCS \ +#define VOCS_AICS_INCLUDED_SERVICE \ IOV_DATA(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28), \ IOV_DATA(0x09, 0x08, \ - 0x0e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x45, 0x18), \ - IOV_DATA(0x08, 0x0f, 0x00, 0xff, 0xff, 0x02, 0x28), \ - IOV_DATA(0x01, 0x08, 0x0f, 0x00, 0x0a) + 0x1e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x45, 0x18, \ + 0x1f, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x43, 0x18), \ + IOV_DATA(0x08, 0x20, 0x00, 0xff, 0xff, 0x02, 0x28), \ + IOV_DATA(0x01, 0x08, 0x20, 0x00, 0x0a) /* ATT: Read By Type Request (0x08) len 6 * Handle range: 0x0001-0x000c @@ -321,6 +328,48 @@ IOV_DATA(0x08, 0x0b, 0x00, 0x0c, 0x00, 0x03, 0x28), \ IOV_DATA(0x01, 0x08, 0x0b, 0x00, 0x0a) + /* + * ATT: Read By Type Request (0x08) len 6 + * Handle range: 0x000d-0x001c + * Attribute type: Characteristic (0x2803) + * + * ATT: Read By Type Response (0x09) len 43 + * Attribute data length: 7 + * Attribute data list: 6 entries + * Handle: 0x000e + * Value: 120f00772b + * Handle: 0x0011 + * Value: 021200782b + * Handle: 0x0013 + * Value: 021400792b + * Handle: 0x0015 + * Value: 1216007a2b + * Handle: 0x0018 + * Value: 0819007b2b + * Handle: 0x001a + * Value: 161b007c2b + * + * ATT: Read By Type Request (0x08) len 6 + * Handle range: 0x001b-0x001c + * Attribute type: Characteristic (0x2803) + * + * ATT: Error Response (0x01) len 4 + * Read By Type Request (0x08) + * Handle: 0x001b + * Error: Attribute Not Found (0x0a) + */ + #define AICS_DISC_CHAR \ + IOV_DATA(0x08, 0x0d, 0x00, 0x1c, 0x00, 0x03, 0x28), \ + IOV_DATA(0x09, 0x07, \ + 0x0e, 0x00, 0x12, 0x0f, 0x00, 0x77, 0x2b, \ + 0x11, 0x00, 0x02, 0x12, 0x00, 0x78, 0x2b, \ + 0x13, 0x00, 0x02, 0x14, 0x00, 0x79, 0x2b, \ + 0x15, 0x00, 0x12, 0x16, 0x00, 0x7a, 0x2b, \ + 0x18, 0x00, 0x08, 0x19, 0x00, 0x7b, 0x2b, \ + 0x1a, 0x00, 0x16, 0x1b, 0x00, 0x7c, 0x2b), \ + IOV_DATA(0x08, 0x1b, 0x00, 0x1c, 0x00, 0x03, 0x28), \ + IOV_DATA(0x01, 0x08, 0x1b, 0x00, 0x0a) + /* ATT: Find Information Request (0x04) len 4 * Handle range: 0x0004-0x0004 * @@ -353,6 +402,39 @@ IOV_DATA(0x04, 0x0c, 0x00, 0x0c, 0x00), \ IOV_DATA(0x05, 0x01, 0x0c, 0x00, 0x02, 0x29) + /* + * ATT: Find Information Request (0x04) len 4 + * Handle range: 0x0010-0x0010 + * + * ATT: Find Information Response (0x05) len 5 + * Format: UUID-16 (0x01) + * Handle: 0x0010 + * UUID: Client Characteristic Configuration (0x2902) + * + * ATT: Find Information Request (0x04) len 4 + * Handle range: 0x0017-0x0017 + * + * ATT: Find Information Response (0x05) len 5 + * Format: UUID-16 (0x01) + * Handle: 0x0017 + * UUID: Client Characteristic Configuration (0x2902) + * + * ATT: Find Information Request (0x04) len 4 + * Handle range: 0x001c-0x001c + * + * ATT: Find Information Response (0x05) len 5 + * Format: UUID-16 (0x01) + * Handle: 0x001c + * UUID: Client Characteristic Configuration (0x2902) + */ + #define AICS_DISC_CHAR_DESC \ + IOV_DATA(0x04, 0x10, 0x00, 0x10, 0x00), \ + IOV_DATA(0x05, 0x01, 0x10, 0x00, 0x02, 0x29), \ + IOV_DATA(0x04, 0x17, 0x00, 0x17, 0x00), \ + IOV_DATA(0x05, 0x01, 0x17, 0x00, 0x02, 0x29), \ + IOV_DATA(0x04, 0x1c, 0x00, 0x1c, 0x00), \ + IOV_DATA(0x05, 0x01, 0x1c, 0x00, 0x02, 0x29) + /* ATT: Read Request (0x0a) len 2 * Handle: 0x0004 * @@ -377,6 +459,1119 @@ IOV_DATA(0x0a, 0x0c, 0x00), \ IOV_DATA(0x0b, 0x00, 0x00) + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x0010 + * ATT: Read Response (0x0b) len 2 + * + * ATT: Read Request (0x0a) len 2 + * Handle: 0x0017 + * ATT: Read Response (0x0b) len 2 + * + * ATT: Read Request (0x0a) len 2 + * Handle: 0x001c + * ATT: Read Response (0x0b) len 2 + */ +#define AICS_READ_CHAR_DESC \ + IOV_DATA(0x0a, 0x10, 0x00), \ + IOV_DATA(0x0b, 0x00, 0x00), \ + IOV_DATA(0x0a, 0x17, 0x00), \ + IOV_DATA(0x0b, 0x00, 0x00), \ + IOV_DATA(0x0a, 0x1c, 0x00), \ + IOV_DATA(0x0b, 0x00, 0x00) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x0012 + * + * ATT: Read Response (0x0b) len 3 + */ +#define AICS_READ_CHAR_GAIN_SETTNG_PROP \ + IOV_DATA(0x0a, 0x12, 0x00), \ + IOV_DATA(0x0b, 0x01, 0x80, 0x7f) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x000f + * + * ATT: Read Response (0x0b) len 4 + */ +#define AICS_READ_CHAR_AUD_IP_STATE \ + IOV_DATA(0x0a, 0x0f, 0x00), \ + IOV_DATA(0x0b, 0x58, 0x00, 0x02, 00) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x000f + * + * ATT: Read Response (0x0b) len 4 + */ +#define AICS_READ_CHAR_AUD_IP_STATE_MUT_DIS \ + IOV_DATA(0x0a, 0x0f, 0x00), \ + IOV_DATA(0x0b, 0x58, 0x02, 0x02, 00) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x000f + * + * ATT: Read Response (0x0b) len 4 + */ +#define AICS_READ_CHAR_AUD_IP_STATE_MUTED \ + IOV_DATA(0x0a, 0x0f, 0x00), \ + IOV_DATA(0x0b, 0x58, 0x01, 0x02, 00) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x000f + * + * ATT: Read Response (0x0b) len 4 + */ +#define AICS_READ_CHAR_AUD_IP_STATE_UNMUTED \ + IOV_DATA(0x0a, 0x0f, 0x00), \ + IOV_DATA(0x0b, 0x58, 0x00, 0x02, 00) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x000f + * + * ATT: Read Response (0x0b) len 4 + */ +#define AICS_READ_CHAR_AUD_IP_STATE_AUTOMATIC \ + IOV_DATA(0x0a, 0x0f, 0x00), \ + IOV_DATA(0x0b, 0x58, 0x00, 0x03, 00) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x000f + * + * ATT: Read Response (0x0b) len 4 + */ +#define AICS_READ_CHAR_AUD_IP_STATE_MANUAL \ + IOV_DATA(0x0a, 0x0f, 0x00), \ + IOV_DATA(0x0b, 0x58, 0x00, 0x02, 00) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x0012 + * + * ATT: Read Response (0x0b) len 3 + */ +#define AICS_READ_CHAR_GAIN_SETTING_PROP \ + IOV_DATA(0x0a, 0x12, 0x00), \ + IOV_DATA(0x0b, 0x01, 0x80, 0x7f) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x0014 + * + * ATT: Read Response (0x0b) len 1 + */ +#define AICS_READ_CHAR_AUD_IP_TYPE \ + IOV_DATA(0x0a, 0x14, 0x00), \ + IOV_DATA(0x0b, 0x01) + + /* + * ATT: Read Request (0x0a) len 2 + * Handle: 0x0014 + * + * ATT: Read Response (0x0b) len 1 + */ +#define AICS_READ_CHAR_AUD_IP_STATUS \ + IOV_DATA(0x0a, 0x16, 0x00), \ + IOV_DATA(0x0b, 0x01) + + /* + * ATT: Write Request (0x12) len 5 + * Handle: 0x0019 + * Data: 016401 + * + * ATT: Error Response (0x01) len 4 + * Write Request (0x12) + * Handle: 0x0019 + * Error: Reserved (0x80) + * + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0265/0366/0467/0568 + * + * ATT: Error Response (0x01) len 4 + * Write Request (0x12) + * Handle: 0x0019 + * Error: Reserved (0x80) + */ +#define AICS_CP_WR_INVLD_CHG_COUNTER \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x64, 0x01), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x80), \ + IOV_DATA(0x12, 0x19, 0x00, 0x02, 0x65), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x80), \ + IOV_DATA(0x12, 0x19, 0x00, 0x03, 0x66), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x80), \ + IOV_DATA(0x12, 0x19, 0x00, 0x04, 0x67), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x80), \ + IOV_DATA(0x12, 0x19, 0x00, 0x05, 0x68), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x80) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0600/ff00 + * + * ATT: Error Response (0x01) len 4 + * Write Request (0x12) + * Handle: 0x0019 + * Error: Reserved (0x81) + */ +#define AICS_CP_WR_INVLD_OP_CODE \ + IOV_DATA(0x12, 0x19, 0x00, 0x06, 0x00), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x81), \ + IOV_DATA(0x12, 0x19, 0x00, 0xff, 0x00), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x81) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0200 + * + * ATT: Error Response (0x01) len 4 + * Write Request (0x12) + * Handle: 0x0019 + * Error: Reserved (0x82) + */ +#define AICS_CP_WR_UNMUTE \ + IOV_DATA(0x12, 0x19, 0x00, 0x02, 0x00), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x82) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0300 + * + * ATT: Error Response (0x01) len 4 + * Write Request (0x12) + * Handle: 0x0019 + * Error: Reserved (0x82) + */ +#define AICS_CP_WR_MUTE \ + IOV_DATA(0x12, 0x19, 0x00, 0x03, 0x00), \ + IOV_DATA(0x01, 0x12, 0x19, 0x00, 0x82) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0200 + * + * ATT: Write Response (0x13) len 0 + */ +#define AICS_CP_WR_UNMUTE_SUCCESS \ + IOV_DATA(0x12, 0x19, 0x00, 0x02, 0x00), \ + IOV_DATA(0x13) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0300 + * + * ATT: Write Response (0x13) len 0 + */ +#define AICS_CP_WR_MUTE_SUCCESS \ + IOV_DATA(0x12, 0x19, 0x00, 0x03, 0x00), \ + IOV_DATA(0x13) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0400 + * + * ATT: Write Response (0x13) len 0 + */ +#define AICS_CP_WR_MANUAL_GAIN \ + IOV_DATA(0x12, 0x19, 0x00, 0x04, 0x00), \ + IOV_DATA(0x13) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0019 + * Data: 0500 + * + * ATT: Write Response (0x13) len 0 + */ +#define AICS_CP_WR_AUTOMATIC_GAIN \ + IOV_DATA(0x12, 0x19, 0x00, 0x05, 0x00), \ + IOV_DATA(0x13) + + /* + * ATT: Write Request (0x12) len 5 + * Handle: 0x0019 + * Data: 01007f + * + * ATT: Write Response (0x13) len 0 + */ +#define AICS_CP_WR_GAIN_SETTING_MAX \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x00, 0x7f), \ + IOV_DATA(0x13) + + /* + * ATT: Write Request (0x12) len 5 + * Handle: 0x0019 + * Data: 010080 + * + * ATT: Write Response (0x13) len 0 + */ +#define AICS_CP_WR_GAIN_SETTING_MIN \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x01, 0x80), \ + IOV_DATA(0x13) + + /* + * ATT: Write Request (0x12) len 4 + * Handle: 0x0010 + * Data: 0100 + * + * ATT: Write Response (0x13) len 0 + */ +#define AICS_ENABLE_AUD_IP_STATE_CC \ + IOV_DATA(0x12, 0x10, 0x00, 0x01, 0x00), \ + IOV_DATA(0X13) + + /* + * ATT: Handle Value Notification (0x1b) len 6 + * Handle: 0x000f + * Data: 58000201 + */ +#define AICS_AUD_IP_STATE_UNMUTED_NOTIF \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x58, 0x00, 0x02, 0x01) + + /* + * ATT: Handle Value Notification (0x1b) len 6 + * Handle: 0x000f + * Data: 58010201 + */ +#define AICS_AUD_IP_STATE_MUTED_NOTIF \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x58, 0x01, 0x02, 0x01) + + /* + * ATT: Handle Value Notification (0x1b) len 6 + * Handle: 0x000f + * Data: 58000201 + * + */ +#define AICS_AUD_IP_STATE_MANUAL_GAIN_NOTIF \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x58, 0x00, 0x02, 0x01) + + /* + * ATT: Handle Value Notification (0x1b) len 6 + * Handle: 0x000f + * Data: 58000301 + */ +#define AICS_AUD_IP_STATE_AUTOMATIC_GAIN_NOTIF \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x58, 0x00, 0x03, 0x01) + + /* + * ATT: Handle Value Notification (0x1b) len 6 + * Handle: 0x000f + * Data: 7f000201 + */ +#define AICS_AUD_IP_STATE_GAIN_SETTING_MAX_NOTIF \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7f, 0x00, 0x02, 0x01) + + /* + * ATT: Handle Value Notification (0x1b) len 6 + * Handle: 0x000f + * Data: 80000202 + */ +#define AICS_AUD_IP_STATE_GAIN_SETTING_MIN_NOTIF \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x80, 0x00, 0x02, 0x02) + + /* + * AICS/SR/CP/BV-01-C + * Test Procedure: + * 1. The Lower Tester executes the GATT Read Characteristic Value + * sub-procedure for the Audio Input State characteristic. + * 2. The Lower Tester executes the GATT Read Characteristic Value + * sub-procedure for the Gain Setting Properties characteristic. + * Repeat steps 3–5 for (255 – Change_Counter value) + 1 times. + * [AICS_CP_WR_GAIN_SETTING_255_LOOP does the above point] + * 3. The Lower Tester executes the GATT Write Characteristic Value + * sub-procedure for the Audio Input Control Point characteristic with + * the Set Gain Setting Opcode, Gain Setting parameter set to a random + * value between the Gain_Setting_Minimum field and Gain_Setting_Maximum + * field values and different than the last iteration, and the + * Change_Counter parameter. + * 4. The Lower Tester receives a Write Response indicating that the IUT + * has accepted the Opcode. + * 5. The Lower Tester receives a GATT Characteristic Value Notification + * for the Audio Input State characteristic. + * + */ +#define AICS_CP_WR_GAIN_SETTING_255_LOOP \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x00, 0xc4), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xc4, 0x00, 0x02, 0x01), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x01, 0xaf), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xaf, 0x00, 0x02, 0x02), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x02, 0xcd), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xcd, 0x00, 0x02, 0x03), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x03, 0xd4), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xd4, 0x00, 0x02, 0x04), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x04, 0x08), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x08, 0x00, 0x02, 0x05), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x05, 0x88), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x88, 0x00, 0x02, 0x06), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x06, 0xde), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xde, 0x00, 0x02, 0x07), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x07, 0x21), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x21, 0x00, 0x02, 0x08), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x08, 0x41), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x41, 0x00, 0x02, 0x09), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x09, 0x08), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x08, 0x00, 0x02, 0x0a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x0a, 0x05), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x05, 0x00, 0x02, 0x0b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x0b, 0x18), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x18, 0x00, 0x02, 0x0c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x0c, 0x28), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x28, 0x00, 0x02, 0x0d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x0d, 0xca), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xca, 0x00, 0x02, 0x0e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x0e, 0xa0), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa0, 0x00, 0x02, 0x0f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x0f, 0xd0), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xd0, 0x00, 0x02, 0x10), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x10, 0xec), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xec, 0x00, 0x02, 0x11), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x11, 0x3c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3c, 0x00, 0x02, 0x12), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x12, 0x09), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x09, 0x00, 0x02, 0x13), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x13, 0x05), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x05, 0x00, 0x02, 0x14), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x14, 0x29), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x29, 0x00, 0x02, 0x15), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x15, 0x28), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x28, 0x00, 0x02, 0x16), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x16, 0x25), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x25, 0x00, 0x02, 0x17), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x17, 0x31), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x31, 0x00, 0x02, 0x18), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x18, 0x49), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x49, 0x00, 0x02, 0x19), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x19, 0x12), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x12, 0x00, 0x02, 0x1a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x1a, 0x09), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x09, 0x00, 0x02, 0x1b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x1b, 0x29), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x29, 0x00, 0x02, 0x1c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x1c, 0x55), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x55, 0x00, 0x02, 0x1d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x1d, 0x35), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x35, 0x00, 0x02, 0x1e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x1e, 0x59), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x59, 0x00, 0x02, 0x1f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x1f, 0x69), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x69, 0x00, 0x02, 0x20), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x20, 0x22), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x22, 0x00, 0x02, 0x21), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x21, 0x79), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x79, 0x00, 0x02, 0x22), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x22, 0x11), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x11, 0x00, 0x02, 0x23), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x23, 0x21), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x21, 0x00, 0x02, 0x24), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x24, 0x0a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x0a, 0x00, 0x02, 0x25), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x25, 0x0b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x0b, 0x00, 0x02, 0x26), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x26, 0x0c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x0c, 0x00, 0x02, 0x27), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x27, 0x0d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x0d, 0x00, 0x02, 0x28), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x28, 0x0e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x0e, 0x00, 0x02, 0x29), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x29, 0x0f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x0f, 0x00, 0x02, 0x2a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x2a, 0x61), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x61, 0x00, 0x02, 0x2b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x2b, 0x63), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x63, 0x00, 0x02, 0x2c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x2c, 0x64), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x64, 0x00, 0x02, 0x2d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x2d, 0x68), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x68, 0x00, 0x02, 0x2e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x2e, 0x14), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x14, 0x00, 0x02, 0x2f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x2f, 0x33), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x33, 0x00, 0x02, 0x30), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x30, 0x31), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x31, 0x00, 0x02, 0x31), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x31, 0x3e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3e, 0x00, 0x02, 0x32), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x32, 0x79), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x79, 0x00, 0x02, 0x33), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x33, 0x99), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x99, 0x00, 0x02, 0x34), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x34, 0x81), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x81, 0x00, 0x02, 0x35), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x35, 0x73), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x73, 0x00, 0x02, 0x36), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x36, 0x75), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x75, 0x00, 0x02, 0x37), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x37, 0x6f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6f, 0x00, 0x02, 0x38), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x38, 0x4e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4e, 0x00, 0x02, 0x39), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x39, 0x1a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x1a, 0x00, 0x02, 0x3a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x3a, 0x1c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x1c, 0x00, 0x02, 0x3b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x3b, 0x2c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x2c, 0x00, 0x02, 0x3c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x3c, 0x5a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5a, 0x00, 0x02, 0x3d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x3d, 0x3d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3d, 0x00, 0x02, 0x3e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x3e, 0x5f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5f, 0x00, 0x02, 0x3f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x3f, 0x6e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6e, 0x00, 0x02, 0x40), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x40, 0x2c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x2c, 0x00, 0x02, 0x41), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x41, 0x7c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7c, 0x00, 0x02, 0x42), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x42, 0x01), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x01, 0x00, 0x02, 0x43), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x43, 0x2b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x2b, 0x00, 0x02, 0x44), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x44, 0xbc), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xbc, 0x00, 0x02, 0x45), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x45, 0x3b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3b, 0x00, 0x02, 0x46), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x46, 0x4c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4c, 0x00, 0x02, 0x47), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x47, 0x3d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3d, 0x00, 0x02, 0x48), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x48, 0x7e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7e, 0x00, 0x02, 0x49), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x49, 0x2f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x2f, 0x00, 0x02, 0x4a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x4a, 0x71), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x71, 0x00, 0x02, 0x4b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x4b, 0x93), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x93, 0x00, 0x02, 0x4c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x4c, 0x6c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6c, 0x00, 0x02, 0x4d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x4d, 0x78), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x78, 0x00, 0x02, 0x4e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x4e, 0x44), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x44, 0x00, 0x02, 0x4f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x4f, 0x83), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x83, 0x00, 0x02, 0x50), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x50, 0x2c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x2c, 0x00, 0x02, 0x51), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x51, 0x7e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7e, 0x00, 0x02, 0x52), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x52, 0x61), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x61, 0x00, 0x02, 0x53), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x53, 0xbb), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xbb, 0x00, 0x02, 0x54), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x54, 0xb1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb1, 0x00, 0x02, 0x55), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x55, 0x3e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3e, 0x00, 0x02, 0x56), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x56, 0xca), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xca, 0x00, 0x02, 0x57), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x57, 0x3f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3f, 0x00, 0x02, 0x58), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x58, 0x3e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3e, 0x00, 0x02, 0x59), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x59, 0xdf), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xdf, 0x00, 0x02, 0x5a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x5a, 0xe1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xe1, 0x00, 0x02, 0x5b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x5b, 0xd3), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xd3, 0x00, 0x02, 0x5c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x5c, 0x9c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9c, 0x00, 0x02, 0x5d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x5d, 0x70), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x70, 0x00, 0x02, 0x5e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x5e, 0xa4), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa4, 0x00, 0x02, 0x5f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x5f, 0x8a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x8a, 0x00, 0x02, 0x60), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x60, 0x7c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7c, 0x00, 0x02, 0x61), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x61, 0x5f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5f, 0x00, 0x02, 0x62), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x62, 0x6a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6a, 0x00, 0x02, 0x63), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x63, 0xb3), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb3, 0x00, 0x02, 0x64), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x64, 0xb7), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb7, 0x00, 0x02, 0x65), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x65, 0x3b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3b, 0x00, 0x02, 0x66), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x66, 0x4a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4a, 0x00, 0x02, 0x67), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x67, 0x3b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3b, 0x00, 0x02, 0x68), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x68, 0x8e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x8e, 0x00, 0x02, 0x69), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x69, 0xef), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xef, 0x00, 0x02, 0x6a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x6a, 0xe5), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xe5, 0x00, 0x02, 0x6b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x6b, 0xe3), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xe3, 0x00, 0x02, 0x6c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x6c, 0x5c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5c, 0x00, 0x02, 0x6d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x6d, 0x79), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x79, 0x00, 0x02, 0x6e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x6e, 0xa1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa1, 0x00, 0x02, 0x6f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x6f, 0x8b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x8b, 0x00, 0x02, 0x70), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x70, 0x5e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5e, 0x00, 0x02, 0x71), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x71, 0x0f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x0f, 0x00, 0x02, 0x72), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x72, 0x6c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6c, 0x00, 0x02, 0x73), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x73, 0xb9), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb9, 0x00, 0x02, 0x74), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x74, 0xb0), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb0, 0x00, 0x02, 0x75), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x75, 0x31), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x31, 0x00, 0x02, 0x76), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x76, 0x4d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4d, 0x00, 0x02, 0x77), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x77, 0x39), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x39, 0x00, 0x02, 0x78), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x78, 0xde), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xde, 0x00, 0x02, 0x79), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x79, 0xe9), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xe9, 0x00, 0x02, 0x7a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x7a, 0xe8), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xe8, 0x00, 0x02, 0x7b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x7b, 0xa3), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa3, 0x00, 0x02, 0x7c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x7c, 0xcc), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xcc, 0x00, 0x02, 0x7d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x7d, 0x89), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x89, 0x00, 0x02, 0x7e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x7e, 0xa9), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa9, 0x00, 0x02, 0x7f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x7f, 0x83), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x83, 0x00, 0x02, 0x80), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x80, 0x4e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4e, 0x00, 0x02, 0x81), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x81, 0x9f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9f, 0x00, 0x02, 0x82), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x82, 0x6f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6f, 0x00, 0x02, 0x83), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x83, 0xb5), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb5, 0x00, 0x02, 0x84), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x84, 0xb4), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb4, 0x00, 0x02, 0x85), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x85, 0x21), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x21, 0x00, 0x02, 0x86), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x86, 0x7d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7d, 0x00, 0x02, 0x87), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x87, 0x29), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x29, 0x00, 0x02, 0x88), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x88, 0x9d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9d, 0x00, 0x02, 0x89), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x89, 0xe1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xe1, 0x00, 0x02, 0x8a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x8a, 0xa8), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa8, 0x00, 0x02, 0x8b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x8b, 0xaa), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xaa, 0x00, 0x02, 0x8c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x8c, 0x1c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x1c, 0x00, 0x02, 0x8d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x8d, 0x59), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x59, 0x00, 0x02, 0x8e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x8e, 0xc9), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xc9, 0x00, 0x02, 0x8f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x8f, 0x33), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x33, 0x00, 0x02, 0x90), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x90, 0xe0), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xe0, 0x00, 0x02, 0x91), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x91, 0xf1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xf1, 0x00, 0x02, 0x92), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x92, 0x6e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6e, 0x00, 0x02, 0x93), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x93, 0xb3), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb3, 0x00, 0x02, 0x94), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x94, 0xb8), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb8, 0x00, 0x02, 0x95), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x95, 0x2f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x2f, 0x00, 0x02, 0x96), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x96, 0x7c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7c, 0x00, 0x02, 0x97), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x97, 0x25), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x25, 0x00, 0x02, 0x98), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x98, 0x9e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9e, 0x00, 0x02, 0x99), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x99, 0xed), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xed, 0x00, 0x02, 0x9a), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x9a, 0xa5), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa5, 0x00, 0x02, 0x9b), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x9b, 0xab), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xab, 0x00, 0x02, 0x9c), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x9c, 0x5c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5c, 0x00, 0x02, 0x9d), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x9d, 0x5c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5c, 0x00, 0x02, 0x9e), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x9e, 0xc8), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xc8, 0x00, 0x02, 0x9f), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0x9f, 0x3b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3b, 0x00, 0x02, 0xa0), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa0, 0xed), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xed, 0x00, 0x02, 0xa1), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa1, 0x9a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9a, 0x00, 0x02, 0xa2), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa2, 0x6b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6b, 0x00, 0x02, 0xa3), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa3, 0xb9), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xb9, 0x00, 0x02, 0xa4), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa4, 0xbe), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xbe, 0x00, 0x02, 0xa5), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa5, 0x2e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x2e, 0x00, 0x02, 0xa6), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa6, 0x7c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7c, 0x00, 0x02, 0xa7), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa7, 0x24), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x24, 0x00, 0x02, 0xa8), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa8, 0x9b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9b, 0x00, 0x02, 0xa9), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xa9, 0xee), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xee, 0x00, 0x02, 0xaa), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xaa, 0xc2), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xc2, 0x00, 0x02, 0xab), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xab, 0xc0), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xc0, 0x00, 0x02, 0xac), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xac, 0x10), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x10, 0x00, 0x02, 0xad), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xad, 0x58), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x58, 0x00, 0x02, 0xae), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xae, 0xa9), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xa9, 0x00, 0x02, 0xaf), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xaf, 0x30), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x30, 0x00, 0x02, 0xb0), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb0, 0x49), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x49, 0x00, 0x02, 0xb1), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb1, 0x90), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x90, 0x00, 0x02, 0xb2), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb2, 0x60), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x60, 0x00, 0x02, 0xb3), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb3, 0xbf), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xbf, 0x00, 0x02, 0xb4), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb4, 0xbd), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xbd, 0x00, 0x02, 0xb5), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb5, 0x33), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x33, 0x00, 0x02, 0xb6), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb6, 0x77), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x77, 0x00, 0x02, 0xb7), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb7, 0x89), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x89, 0x00, 0x02, 0xb8), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb8, 0x9f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9f, 0x00, 0x02, 0xb9), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xb9, 0xf1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xf1, 0x00, 0x02, 0xba), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xba, 0xaa), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xaa, 0x00, 0x02, 0xbb), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xbb, 0xac), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xac, 0x00, 0x02, 0xbc), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xbc, 0x1e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x1e, 0x00, 0x02, 0xbd), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xbd, 0x58), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x58, 0x00, 0x02, 0xbe), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xbe, 0xcd), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xcd, 0x00, 0x02, 0xbf), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xbf, 0x3a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x3a, 0x00, 0x02, 0xc0), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc0, 0x48), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x48, 0x00, 0x02, 0xc1), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc1, 0x9a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x9a, 0x00, 0x02, 0xc2), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc2, 0x6c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6c, 0x00, 0x02, 0xc3), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc3, 0x7a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7a, 0x00, 0x02, 0xc4), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc4, 0x7d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7d, 0x00, 0x02, 0xc5), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc5, 0x7c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7c, 0x00, 0x02, 0xc6), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc6, 0x7d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7d, 0x00, 0x02, 0xc7), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc7, 0x7e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7e, 0x00, 0x02, 0xc8), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc8, 0x7f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x7f, 0x00, 0x02, 0xc9), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xc9, 0xf0), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xf0, 0x00, 0x02, 0xca), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xca, 0xf1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xf1, 0x00, 0x02, 0xcb), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xcb, 0xf2), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xf2, 0x00, 0x02, 0xcc), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xcc, 0xf3), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xf3, 0x00, 0x02, 0xcd), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xcd, 0xf4), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xf4, 0x00, 0x02, 0xce), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xce, 0xc1), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xc1, 0x00, 0x02, 0xcf), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xcf, 0xc2), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0xc2, 0x00, 0x02, 0xd0), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd0, 0x4f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4f, 0x00, 0x02, 0xd1), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd1, 0x60), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x60, 0x00, 0x02, 0xd2), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd2, 0x6a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6a, 0x00, 0x02, 0xd3), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd3, 0x6b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6b, 0x00, 0x02, 0xd4), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd4, 0x6c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6c, 0x00, 0x02, 0xd5), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd5, 0x6d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6d, 0x00, 0x02, 0xd6), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd6, 0x6e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6e, 0x00, 0x02, 0xd7), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd7, 0x6f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x6f, 0x00, 0x02, 0xd8), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd8, 0x91), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x91, 0x00, 0x02, 0xd9), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xd9, 0x92), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x92, 0x00, 0x02, 0xda), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xda, 0x93), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x93, 0x00, 0x02, 0xdb), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xdb, 0x94), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x94, 0x00, 0x02, 0xdc), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xdc, 0x95), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x95, 0x00, 0x02, 0xdd), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xdd, 0x96), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x96, 0x00, 0x02, 0xde), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xde, 0x97), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x97, 0x00, 0x02, 0xdf), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xdf, 0x98), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x98, 0x00, 0x02, 0xe0), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe0, 0x59), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x59, 0x00, 0x02, 0xe1), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe1, 0x5a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5a, 0x00, 0x02, 0xe2), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe2, 0x5b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5b, 0x00, 0x02, 0xe3), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe3, 0x5c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5c, 0x00, 0x02, 0xe4), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe4, 0x5d), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5d, 0x00, 0x02, 0xe5), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe5, 0x5e), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5e, 0x00, 0x02, 0xe6), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe6, 0x5f), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x5f, 0x00, 0x02, 0xe7), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe7, 0x70), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x70, 0x00, 0x02, 0xe8), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe8, 0x71), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x71, 0x00, 0x02, 0xe9), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xe9, 0x72), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x72, 0x00, 0x02, 0xea), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xea, 0x73), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x73, 0x00, 0x02, 0xeb), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xeb, 0x74), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x74, 0x00, 0x02, 0xec), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xec, 0x4a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4a, 0x00, 0x02, 0xed), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xed, 0x4b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4b, 0x00, 0x02, 0xee), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xee, 0x4c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x4c, 0x00, 0x02, 0xef), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xef, 0x50), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x50, 0x00, 0x02, 0xf0), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf0, 0x40), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x40, 0x00, 0x02, 0xf1), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf1, 0x30), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x30, 0x00, 0x02, 0xf2), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf2, 0x50), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x50, 0x00, 0x02, 0xf3), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf3, 0x60), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x60, 0x00, 0x02, 0xf4), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf4, 0x10), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x10, 0x00, 0x02, 0xf5), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf5, 0x11), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x11, 0x00, 0x02, 0xf6), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf6, 0x12), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x12, 0x00, 0x02, 0xf7), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf7, 0x13), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x13, 0x00, 0x02, 0xf8), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf8, 0x14), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x14, 0x00, 0x02, 0xf9), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xf9, 0x15), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x15, 0x00, 0x02, 0xfa), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xfa, 0x16), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x16, 0x00, 0x02, 0xfb), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xfb, 0x18), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x18, 0x00, 0x02, 0xfc), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xfc, 0x19), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x19, 0x00, 0x02, 0xfd), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xfd, 0x1a), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x1a, 0x00, 0x02, 0xfe), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xfe, 0x1b), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x1b, 0x00, 0x02, 0xff), \ + IOV_DATA(0x12, 0x19, 0x00, 0x01, 0xff, 0x1c), \ + IOV_DATA(0x13), \ + IOV_DATA(0x1b, 0x0f, 0x00, 0x1c, 0x00, 0x02, 0x00) + /* ATT: Read Request (0x0a) len 2 * Handle: 0x0003 * @@ -484,14 +1679,105 @@ IOV_DATA(0x13) #define VOCS_SR_SGGIT_CHA_TST_CMDS \ - VOCS_EXCHANGE_MTU, \ - VOCS_PRIMARY_SERVICE_VCS, \ - VOCS_SECONDARY_SERVICE_VOCS, \ - VOCS_INCLUDED_SERVICE_VOCS, \ + VCS_EXCHANGE_MTU, \ + VOCS_AICS_PRIMARY_SERVICE_VCS, \ + VOCS_AICS_SECONDARY_SERVICE, \ + VOCS_AICS_INCLUDED_SERVICE, \ VOCS_DISC_CHAR, \ VOCS_DISC_CHAR_DESC, \ VOCS_READ_CHAR_DESC +#define AICS_SR_SGGIT_CHA_TST_CMDS \ + VCS_EXCHANGE_MTU, \ + VOCS_AICS_PRIMARY_SERVICE_VCS, \ + VOCS_AICS_SECONDARY_SERVICE, \ + VOCS_AICS_INCLUDED_SERVICE, \ + AICS_DISC_CHAR, \ + AICS_DISC_CHAR_DESC, \ + AICS_READ_CHAR_DESC + +#define AICS_SR_SGGIT_CHA_BV_01_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_READ_CHAR_AUD_IP_STATE + +#define AICS_SR_SGGIT_CHA_BV_02_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_READ_CHAR_GAIN_SETTING_PROP + +#define AICS_SR_SGGIT_CHA_BV_03_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_READ_CHAR_AUD_IP_TYPE + +#define AICS_SR_SGGIT_CHA_BV_04_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_READ_CHAR_AUD_IP_STATUS + +#define AICS_SR_SGGIT_CHA_BV_05_C \ + AICS_SR_SGGIT_CHA_TST_CMDS + +#define AICS_SR_SGGIT_CHA_BV_06_C \ + AICS_SR_SGGIT_CHA_TST_CMDS + +#define AICS_SR_SGGIT_CP_BI_01_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_READ_CHAR_AUD_IP_STATE, \ + AICS_CP_WR_INVLD_CHG_COUNTER + +#define AICS_SR_SGGIT_CP_BI_02_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_CP_WR_INVLD_OP_CODE + +#define AICS_SR_SGGIT_CP_BI_03_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_READ_CHAR_AUD_IP_STATE_MUT_DIS, \ + AICS_CP_WR_UNMUTE, \ + AICS_CP_WR_MUTE + +#define AICS_SR_CP_BV_01_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_ENABLE_AUD_IP_STATE_CC, \ + AICS_READ_CHAR_AUD_IP_STATE, \ + AICS_READ_CHAR_GAIN_SETTNG_PROP, \ + AICS_CP_WR_GAIN_SETTING_255_LOOP + +#define AICS_SR_CP_BV_02_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_ENABLE_AUD_IP_STATE_CC, \ + AICS_READ_CHAR_AUD_IP_STATE_MUTED, \ + AICS_CP_WR_UNMUTE_SUCCESS, \ + AICS_AUD_IP_STATE_UNMUTED_NOTIF + +#define AICS_SR_CP_BV_03_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_ENABLE_AUD_IP_STATE_CC, \ + AICS_READ_CHAR_AUD_IP_STATE_UNMUTED, \ + AICS_CP_WR_MUTE_SUCCESS, \ + AICS_AUD_IP_STATE_MUTED_NOTIF + +#define AICS_SR_CP_BV_04_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_ENABLE_AUD_IP_STATE_CC, \ + AICS_READ_CHAR_AUD_IP_STATE_AUTOMATIC, \ + AICS_CP_WR_MANUAL_GAIN, \ + AICS_AUD_IP_STATE_MANUAL_GAIN_NOTIF + +#define AICS_SR_CP_BV_05_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_ENABLE_AUD_IP_STATE_CC, \ + AICS_READ_CHAR_AUD_IP_STATE_MANUAL, \ + AICS_CP_WR_AUTOMATIC_GAIN, \ + AICS_AUD_IP_STATE_AUTOMATIC_GAIN_NOTIF + +#define AICS_SR_SPE_BI_01_C \ + AICS_SR_SGGIT_CHA_TST_CMDS, \ + AICS_ENABLE_AUD_IP_STATE_CC, \ + AICS_READ_CHAR_AUD_IP_STATE, \ + AICS_READ_CHAR_GAIN_SETTNG_PROP, \ + AICS_CP_WR_GAIN_SETTING_MAX, \ + AICS_AUD_IP_STATE_GAIN_SETTING_MAX_NOTIF, \ + AICS_CP_WR_GAIN_SETTING_MIN, \ + AICS_AUD_IP_STATE_GAIN_SETTING_MIN_NOTIF + #define VOCS_SR_SGGIT_SER_BV_01_C \ VOCS_SR_SGGIT_CHA_TST_CMDS @@ -1315,11 +2601,11 @@ IOV_DATA(0x13), \ IOV_DATA(0x1b, 0x03, 0x00, 0x0c, 0xff, 0x00) -int main(int argc, char *argv[]) +static void test_vocs_unit_testcases(void) { - tester_init(&argc, &argv); - - /* VOCS Unit Testcases */ + /* + * VOCS Unit Testcases + */ define_test("VOCS/SR/SGGIT/SER/BV-01-C", test_server, NULL, VOCS_SR_SGGIT_SER_BV_01_C); @@ -1344,6 +2630,131 @@ define_test("VOCS/SR/CP/BV-01-C", test_server, NULL, VOCS_SR_CP_BV_01_C); +} + +static void test_aics_unit_testcases(void) +{ + /* + * AICS Unit Testcases + */ + define_test("AICS/SR/SGGIT/CHA/BV-01-C", test_server, NULL, + AICS_SR_SGGIT_CHA_BV_01_C); + + define_test("AICS/SR/SGGIT/CHA/BV-02-C", test_server, NULL, + AICS_SR_SGGIT_CHA_BV_02_C); + + define_test("AICS/SR/SGGIT/CHA/BV-03-C", test_server, NULL, + AICS_SR_SGGIT_CHA_BV_03_C); + + define_test("AICS/SR/SGGIT/CHA/BV-04-C", test_server, NULL, + AICS_SR_SGGIT_CHA_BV_04_C); + + define_test("AICS/SR/SGGIT/CHA/BV-05-C", test_server, NULL, + AICS_SR_SGGIT_CHA_BV_05_C); + + define_test("AICS/SR/SGGIT/CHA/BV-06-C", test_server, NULL, + AICS_SR_SGGIT_CHA_BV_06_C); + + define_test("AICS/SR/SGGIT/CP/BI-01-C", test_server, NULL, + AICS_SR_SGGIT_CP_BI_01_C); + + define_test("AICS/SR/SGGIT/CP/BI-02-C", test_server, NULL, + AICS_SR_SGGIT_CP_BI_02_C); + + /* AICS/SR/SGGIT/CP/BI-03-C: + * In function *aics_new(struct gatt_db *db)[src/shared/vcp.c] + * by default state of the 'aics_aud_ip_st->mute' is set to + * AICS_NOT_MUTED[0x00];. + * As per test specs, Testcase AICS/SR/SGGIT/CP/BI-03-C, Initial + * condition of mute state should be AICS_DISABLED[0x02]. + * To verify this Unit test case we have to modify the initial + * state of 'aics_aud_ip_st->mute' to AICS_DISABLED in code + * [in func aics_new()], build it and run bluetoothd. Then run + * this unit test case and this test case will Pass. + */ + /* define_test("AICS/SR/SGGIT/CP/BI-03-C", test_server, NULL, + * AICS_SR_SGGIT_CP_BI_03_C); + */ + + /* AICS/SR/SGGIT/CP/BI-04-C - TO-DO Need to give two times input + * from user during test case run + */ + + define_test("AICS/SR/CP/BV-01-C", test_server, NULL, + AICS_SR_CP_BV_01_C); + + /* AICS/SR/CP/BV-02-C: + * In function *aics_new(struct gatt_db *db)[src/shared/vcp.c] + * by default state of the 'aics_aud_ip_st->mute' is set to + * AICS_NOT_MUTED[0x00];. + * As per test specs, Testcase AICS/SR/CP/BV-02-C, Initial + * condition of mute state should be AICS_MUTED[0x01]. + * To verify this Unit test case we have to modify the initial + * state of 'aics_aud_ip_st->mute' to AICS_MUTED in code + * [in func aics_new()], build it and run bluetoothd. Then run + * this unit test case and this test case will Pass. + */ + /* define_test("AICS/SR/CP/BV-02-C", test_server, NULL, + * AICS_SR_CP_BV_02_C); + */ + + /* AICS/SR/CP/BV-03-C: + * In function *aics_new(struct gatt_db *db)[src/shared/vcp.c] + * by default state of the 'aics_aud_ip_st->mute' is set to + * AICS_NOT_MUTED[0x00];. + * As per test specs, Testcase AICS/SR/CP/BV-03-C, Initial + * condition of mute state should be AICS_NOT_MUTED[0x00]. + * If you have changed this value to some other value, then + * To verify this Unit test case you have to modify the initial + * state of 'aics_aud_ip_st->mute' to AICS_NOT_MUTED in code + * [in func aics_new()], build it and run bluetoothd. Then run + * this unit test case and this test case will Pass. + */ + /* define_test("AICS/SR/CP/BV-03-C", test_server, NULL, + * AICS_SR_CP_BV_03_C); + */ + + /* AICS/SR/CP/BV-04-C: + * In function *aics_new(struct gatt_db *db)[src/shared/vcp.c] + * by default state of the 'aics_aud_ip_st->gain_mode' is set to + * AICS_GAIN_MODE_MANUAL[0x02];. + * As per test specs, Testcase AICS/SR/CP/BV-04-C, Initial + * value of gain mode field, should be AICS_GAIN_MODE_AUTO[0x03]. + * To verify this Unit test case you have to modify the initial + * state of 'aics_aud_ip_st->gain_mode' to AICS_GAIN_MODE_AUTO in code + * [in func aics_new()], build it and run bluetoothd. Then run + * this unit test case and this test case will Pass. + */ + /* define_test("AICS/SR/CP/BV-04-C", test_server, NULL, + * AICS_SR_CP_BV_04_C); + */ + + /* AICS/SR/CP/BV-05-C: + * In function *aics_new(struct gatt_db *db)[src/shared/vcp.c] + * by default state of the 'aics_aud_ip_st->gain_mode' is set to + * AICS_GAIN_MODE_MANUAL[0x02];. + * As per test specs, Testcase AICS/SR/CP/BV-05-C, Initial + * value of gain mode field, should be AICS_GAIN_MODE_MANUAL[0x02]. + * If you have changed this value to some other value, then + * To verify this Unit test case you have to modify the initial + * state of 'aics_aud_ip_st->gain_mode' to AICS_GAIN_MODE_MANUAL in code + * [in func aics_new()], build it and run bluetoothd. Then run + * this unit test case and this test case will Pass. + */ + /* define_test("AICS/SR/CP/BV-05-C", test_server, NULL, + * AICS_SR_CP_BV_05_C); + */ + define_test("AICS/SR/SPE/BI-01-C", test_server, NULL, + AICS_SR_SPE_BI_01_C); + + +} +int main(int argc, char *argv[]) +{ + tester_init(&argc, &argv); + + test_vocs_unit_testcases(); + test_aics_unit_testcases(); return tester_run(); }