-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bluetooth: Audio: PAC location refactor #42593
Bluetooth: Audio: PAC location refactor #42593
Conversation
Depends on #42314 |
f07e161
to
d07b820
Compare
5fae16d
to
0127753
Compare
enum bt_audio_location { | ||
BT_AUDIO_LOCATION_FRONT_LEFT = BIT(0), | ||
BT_AUDIO_LOCATION_FRONT_RIGHT = BIT(1), | ||
BT_AUDIO_LOCATION_FRONT_CENTER = BIT(2), | ||
BT_AUDIO_LOCATION_LOW_FREQ_EFFECTS_1 = BIT(3), | ||
BT_AUDIO_LOCATION_BACK_LEFT = BIT(4), | ||
BT_AUDIO_LOCATION_BACK_RIGHT = BIT(5), | ||
BT_AUDIO_LOCATION_FRONT_LEFT_OF_CENTER = BIT(6), | ||
BT_AUDIO_LOCATION_FRONT_RIGHT_OF_CENTER = BIT(7), | ||
BT_AUDIO_LOCATION_BACK_CENTER = BIT(8), | ||
BT_AUDIO_LOCATION_LOW_FREQ_EFFECTS_2 = BIT(9), | ||
BT_AUDIO_LOCATION_SIDE_LEFT = BIT(10), | ||
BT_AUDIO_LOCATION_SIDE_RIGHT = BIT(11), | ||
BT_AUDIO_LOCATION_TOP_FRONT_LEFT = BIT(12), | ||
BT_AUDIO_LOCATION_TOP_FRONT_RIGHT = BIT(13), | ||
BT_AUDIO_LOCATION_TOP_FRONT_CENTER = BIT(14), | ||
BT_AUDIO_LOCATION_TOP_CENTER = BIT(15), | ||
BT_AUDIO_LOCATION_TOP_BACK_LEFT = BIT(16), | ||
BT_AUDIO_LOCATION_TOP_BECK_RIGHT = BIT(17), | ||
BT_AUDIO_LOCATION_TOP_SIDE_LEFT = BIT(18), | ||
BT_AUDIO_LOCATION_TOP_SIDE_RIGHT = BIT(19), | ||
BT_AUDIO_LOCATION_TOP_BACK_CENTER = BIT(20), | ||
BT_AUDIO_LOCATION_BOTTOM_FRONT_CENTER = BIT(21), | ||
BT_AUDIO_LOCATION_BOTTOM_FRONT_LEFT = BIT(22), | ||
BT_AUDIO_LOCATION_BOTTOM_FRONT_RIGHT = BIT(23), | ||
BT_AUDIO_LOCATION_FRONT_LEFT_WIDE = BIT(24), | ||
BT_AUDIO_LOCATION_FRONT_RIGHT_WIDE = BIT(25), | ||
BT_AUDIO_LOCATION_LEFT_SURROUND = BIT(26), | ||
BT_AUDIO_LOCATION_RIGHT_SURROUND = BIT(27), | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though it makes sense to group these together, I do not think using an enum is a good way to do it.
Enums contains a finite number of possible values - hence you would need to suppress compiler warnings to be able to OR multiple values together - or you would need to typecast the result back to the enum type - which could result in errors if the compiler have made assumptions about the finite number of values (for example a switch statement do not need a default case when doing switch on an enum type and all possible cases are handled). I do not know if newer c-versions have support for this though?
I have seen others define a type and then typecast to this in the define:
typedef uint32_t bt_audio_loc_t
#define BT_AUDIO_LOCATION_FRONT_LEFT ((bt_audio_loc_t) BIT(0))
then you can use bt_audio_loc_t rather than uint32_t for function parameters that expect audio location(s). And you can freely OR multiple together as the result would still be uint32_t.
To group them together in the API documentation use the @name Audio Locations @{ ... @}
grouping feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hear your concerns and see what issues this may cause.
The reasoning behind this approach is:
- (named)
enum
s provide an easy way for an API to express the "group" of values expected, without relying on doxygen-specific grouping - https://www.kernel.org/doc/html/v4.10/process/coding-style.html#macros-enums-and-rtl (which we follow) express that
enum
s are preferred for groups of constants - This is the approach (although without the name) used by e.g.
BT_LE_ADV_OPT_
, and other optionenum
s inbluetooth.h
.
If we see compiler issues, then this is indeed something we need to fix. If the issue is caused by using the enum
type in the APIs, then we can replace the enum
type with uint32_t
(similar to how the BT_LE_ADV_OPT_
are used), and simply add a reference to the enum
in the function description.
If the issue is caused by just using the enum
keyword, then this problem exist in many places in the code base, and we need a general fix as well as an exception to the Linux kernel code guideline.
Do you have any references to the compiler issues (warnings) you are experiencing that I can read?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is an accepted way to do it in Zephyr, I'm OK with it.
I do not have a reference, but in my early days I was told to refactor a full FM radio API I made due to this issue (not a GCC based compiler) - hence I always try to avoid using enums when I specify bit-values.
I had a look at the different enum related warnings that GCC support, and non of them covers this issue, hence I think we can go ahead using enums in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise refactoring to a group of #define
s can be done without breaking the API if we just change the API from enum bt_audio_location
to uint32_t bt_audio_location
.
@@ -46,7 +46,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_ASCS ascs.c) | |||
zephyr_library_sources_ifdef(CONFIG_BT_PACS pacs.c) | |||
zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_STREAM stream.c) | |||
zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_UNICAST_SERVER unicast_server.c) | |||
zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_CAPABILITIES capabilities.c) | |||
zephyr_library_sources_ifdef(CONFIG_bt_audio_capability capabilities.c) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong renaming I think?
Same below
0127753
to
894bc92
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
894bc92
to
b6ebfe1
Compare
b6ebfe1
to
10186d8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@asbjornsabo @MariuszSkamra @jhedberg This is blocking other development, will one (or more) of you please review? |
subsys/bluetooth/audio/pacs.c
Outdated
|
||
if (unicast_server_cb == NULL || | ||
unicast_server_cb->publish_location == NULL) { | ||
BT_WARN("Not callback for publish_location"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not -> No?
(Other places as well)
subsys/bluetooth/audio/pacs.c
Outdated
} | ||
|
||
return bt_gatt_attr_read(conn, attr, buf, len, offset, | ||
&sys_cpu_to_le32(location_32), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the result have to be assigned to a variable before applying the &
operator (so that the operand is an "lvalue")?
(Also elsewhere)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i.e. if we need to do something like
uint32_t le_location = sys_cpu_to_le32(location_32);
return bt_gatt_attr_read(conn, attr, buf, len, offset,
&le_location, sizeof(le_location));
instead?
I actually do not really know. It doesn't throw any errors.
Keep in mind that sys_cpu_to_le32
is a macro, and for LE it will be
#define sys_cpu_to_le32(val) (val)
and
#define sys_cpu_to_le32(val) __bswap_32(val)
for BE. Not sure if this will give an error for BE, and I am not sure how I can test it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to use the same thing in some code here, and I get compile errors for both variants.
In my understanding, this should not work - I think one ends up with a value, not a variable, and one can take take the address of a value.
Will check again - I may not have tested correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested again. This time it worked for the "le" variant, but fails for the "be" variant.
uint32_t location;
uint32_t *p;
location = 7;
p = &sys_cpu_to_be32(location);
gives
error: lvalue required as unary '&' operand
324 | p = &sys_cpu_to_be32(location);
| ^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
10186d8
to
d4324fa
Compare
include/bluetooth/audio/audio.h
Outdated
/** @brief Audio Capability type */ | ||
enum bt_audio_pac_type { | ||
BT_AUDIO_SINK = 0x01, | ||
BT_AUDIO_SOURCE = 0x02, | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you move the code only, but I saw this is used for ASE direction as well. Maybe the enum name should be different then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. Do you have any suggestions? bt_audio_type
seems a bit too generic for my taste. bt_audio_ep_type
perhaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's fix this in a new issue: #43693
* | ||
* Publish location callback is called whenever a remote client | ||
* requests to read the Published Audio Capabilities (PAC) location, | ||
* or if the location needs to be notified. | ||
* | ||
* @param[in] conn The connection that requests the location. | ||
* Will be NULL if the location is requested | ||
* for sending a notification, as a result of | ||
* callling | ||
* bt_audio_unicast_server_location_changed(). | ||
* @param[in] type Type of the endpoint. | ||
* @param[out] location Pointer to the location that needs to be set. | ||
* | ||
* @return 0 in case of success or negative value in case of error. | ||
*/ | ||
int (*publish_location)(struct bt_conn *conn, | ||
enum bt_audio_pac_type type, | ||
enum bt_audio_location *location); | ||
|
||
/** @brief Write location callback | ||
* | ||
* Write location callback is called whenever a remote client | ||
* requests to write the Published Audio Capabilities (PAC) location. | ||
* | ||
* @param conn The connection that requests the write. | ||
* @param type Type of the endpoint. | ||
* @param location The location being written. | ||
* | ||
* @return 0 in case of success or negative value in case of error. | ||
*/ | ||
int (*write_location)(struct bt_conn *conn, enum bt_audio_pac_type type, | ||
enum bt_audio_location location); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the location cannot be stored internally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can, however PACS was (is) designed to be stateless, i.e. all the values are located in an upper layer.
It would have been easy to store the locaiton in PACS and still keep e.g. the capabilities in an upper layer, but I did not want to mix-match where the service data/states were stored, so used the same approach for the location as we have for the other values in PACS.
This of course is a more complex implementation and possibly erroneous as the notifications when the location is changed is no longer ensured by the stack.
We have yet to decided what final approach we want to take for the LE Audio services, as both approaches have their pros and cons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe an application can change the value run-time - hence I think this implementation is how it should be.
But reading the description again I think:
* Write location callback is called whenever a remote client
* requests to write the Published Audio Capabilities (PAC) location.
should be:
* Write location callback is called whenever a remote client
* requests to read the Published Audio Capabilities (PAC) location.
PAC's are read only and my assumption was that the callback was called write as the application (PAC server) needs to write the value that the client wants to read?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Casper-Bonde-Bose No, the write
description is correct. The Sink and Source location characteristics are (optionally) writeable:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see - Maybe a feature to be able to configure a speaker to be at a specific location?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly, yes. We (@asbjornsabo and I) raised this in the GAWG some time ago, and it seems that in hindsight it shouldn't have been writable.
d4324fa
to
adfdca2
Compare
Refactor the PAC location read and write. Instead of storing the location in the service, the location is now stored in the application, and is retrieved by the service via callbacks. Similarly, if a client writes the location, this request is being sent to the application. Signed-off-by: Emil Gydesen <[email protected]>
adfdca2
to
19f6c24
Compare
Re-pushed to fix a bad merge conflict fix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comments have been addressed.
The location values in PACs were build-time initiated and unchangable by the server itself.
This PR adds support for changing the value in the server, as the value will live in the server (currently implemented in the capabilities.c layer), instead of in the service.
This also adds notification for the value when it is changed, by either the server or a remove client.
fixes #42342
fixes #42343