rust/bt-ascs: Initial implementaiton

Audio Stream Control Service crate, including initial decoding of
control point operaitons and initially empty Events from the service.

Test: added unit tests, cargo test
Change-Id: I1945b5e306783a6b514d6da8ba5dda66fcf7b8f0
Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/1722
Commit-Queue: Marie Janssen <jamuraa@google.com>
Reviewed-by: Dayeong Lee <dayeonglee@google.com>
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 650f02c..ddc7870 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -24,6 +24,7 @@
 assert_matches = "1.5.0"
 bitfield = "0.14.0"
 futures = "=0.3.31"
+futures-test = "=0.3.31"
 lazy_static = "1.5"
 log = { version = "0.4.27", features = [ "kv", "std" ] }
 num = { version = "0.4.3", features = ["rand"] }
diff --git a/rust/bt-ascs/Cargo.toml b/rust/bt-ascs/Cargo.toml
new file mode 100644
index 0000000..723aa6f
--- /dev/null
+++ b/rust/bt-ascs/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "bt-ascs"
+version = "0.0.1"
+license.workspace = true
+edition.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+bt-common.workspace = true
+bt-gatt.workspace = true
+thiserror.workspace = true
+futures.workspace = true
+pin-project.workspace = true
+log.workspace = true
+
+[dev-dependencies]
+futures-test.workspace = true
+bt-gatt = { workspace = true, features = ["test-utils"] }
diff --git a/rust/bt-ascs/LICENSE b/rust/bt-ascs/LICENSE
new file mode 120000
index 0000000..30cff74
--- /dev/null
+++ b/rust/bt-ascs/LICENSE
@@ -0,0 +1 @@
+../../LICENSE
\ No newline at end of file
diff --git a/rust/bt-ascs/src/lib.rs b/rust/bt-ascs/src/lib.rs
new file mode 100644
index 0000000..f3f7380
--- /dev/null
+++ b/rust/bt-ascs/src/lib.rs
@@ -0,0 +1,10 @@
+// Copyright 2024 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+pub mod server;
+pub mod types;
+pub use types::Error;
+
+#[cfg(test)]
+pub mod tests;
diff --git a/rust/bt-ascs/src/server.rs b/rust/bt-ascs/src/server.rs
new file mode 100644
index 0000000..12b8274
--- /dev/null
+++ b/rust/bt-ascs/src/server.rs
@@ -0,0 +1,378 @@
+// Copyright 2024 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use bt_gatt::Characteristic;
+use bt_gatt::server::{ReadResponder, WriteResponder};
+use bt_gatt::types::{
+    AttributePermissions, CharacteristicProperty, GattError, Handle, SecurityLevels,
+};
+use futures::task::{Poll, Waker};
+use futures::{Future, Stream, stream::FusedStream};
+use pin_project::pin_project;
+use std::collections::HashMap;
+
+use bt_common::{PeerId, Uuid};
+use bt_gatt::server::{LocalService, Server, ServiceDefinition, ServiceId};
+
+use crate::types::*;
+
+#[pin_project(project = LocalServiceProj)]
+enum LocalServiceState<T: bt_gatt::ServerTypes> {
+    NotPublished {
+        waker: Option<Waker>,
+    },
+    Preparing {
+        #[pin]
+        fut: T::LocalServiceFut,
+    },
+    Published {
+        service: T::LocalService,
+        #[pin]
+        events: T::ServiceEventStream,
+    },
+    Terminated,
+}
+
+impl<T: bt_gatt::ServerTypes> Default for LocalServiceState<T> {
+    fn default() -> Self {
+        Self::NotPublished { waker: None }
+    }
+}
+
+impl<T: bt_gatt::ServerTypes> LocalServiceState<T> {
+    fn service(&self) -> Option<&T::LocalService> {
+        let Self::Published { service, .. } = self else {
+            return None;
+        };
+        Some(service)
+    }
+}
+
+impl<T: bt_gatt::ServerTypes> Stream for LocalServiceState<T> {
+    type Item = Result<bt_gatt::server::ServiceEvent<T>, Error>;
+
+    fn poll_next(
+        mut self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> Poll<Option<Self::Item>> {
+        // SAFETY:
+        //  - Wakers are Unpin
+        //  - We re-pin the structurally pinned futures in Preparing and Published
+        //    (service is untouched)
+        //  - Terminated is empty
+        loop {
+            match self.as_mut().project() {
+                LocalServiceProj::Terminated => return Poll::Ready(None),
+                LocalServiceProj::NotPublished { .. } => {
+                    self.as_mut()
+                        .set(LocalServiceState::NotPublished { waker: Some(cx.waker().clone()) });
+                    return Poll::Pending;
+                }
+                LocalServiceProj::Preparing { fut } => {
+                    let service_result = futures::ready!(fut.poll(cx));
+                    let Ok(service) = service_result else {
+                        self.as_mut().set(LocalServiceState::Terminated);
+                        return Poll::Ready(Some(Err(Error::PublishError(
+                            service_result.err().unwrap(),
+                        ))));
+                    };
+                    let events = service.publish();
+                    self.as_mut().set(LocalServiceState::Published { service, events });
+                    continue;
+                }
+                LocalServiceProj::Published { service: _, events } => {
+                    let item = futures::ready!(events.poll_next(cx));
+                    let Some(gatt_result) = item else {
+                        self.as_mut().set(LocalServiceState::Terminated);
+                        return Poll::Ready(Some(Err(Error::PublishError(
+                            "GATT server terminated".into(),
+                        ))));
+                    };
+                    let Ok(event) = gatt_result else {
+                        self.as_mut().set(LocalServiceState::Terminated);
+                        return Poll::Ready(Some(Err(Error::PublishError(
+                            gatt_result.err().unwrap(),
+                        ))));
+                    };
+                    return Poll::Ready(Some(Ok(event)));
+                }
+            }
+        }
+    }
+}
+
+impl<T: bt_gatt::ServerTypes> FusedStream for LocalServiceState<T> {
+    fn is_terminated(&self) -> bool {
+        match self {
+            Self::Terminated => true,
+            _ => false,
+        }
+    }
+}
+
+impl<T: bt_gatt::ServerTypes> LocalServiceState<T> {
+    fn is_not_published(&self) -> bool {
+        matches!(self, LocalServiceState::NotPublished { .. })
+    }
+}
+
+#[pin_project]
+pub struct AudioStreamControlServiceServer<T: bt_gatt::ServerTypes> {
+    service_def: ServiceDefinition,
+    #[pin]
+    local_service: LocalServiceState<T>,
+    default_client_endpoints: ClientEndpoints,
+    client_endpoints: HashMap<PeerId, ClientEndpoints>,
+}
+
+const CONTROL_POINT_HANDLE: Handle = Handle(1);
+const BASE_ENDPOINT_HANDLE: Handle = Handle(2);
+
+pub const ASCS_UUID: Uuid = Uuid::from_u16(0x184E);
+
+// As only one ASCS service is allowed on a host, define an arbitrary ServiceId
+// that every AudioStreamControlServiceServer will attempt to use so publishing
+// multiple will fail.
+pub(crate) const ASCS_SERVICE_ID: ServiceId = ServiceId::new(1123901);
+
+impl<T: bt_gatt::ServerTypes> AudioStreamControlServiceServer<T> {
+    pub fn new(source_count: u8, sink_count: u8) -> Self {
+        let default_client_endpoints = ClientEndpoints::new(source_count, sink_count);
+        let mut chars: Vec<Characteristic> = (&default_client_endpoints).into();
+        chars.push(Self::build_control_point());
+        let mut service_def = ServiceDefinition::new(
+            ASCS_SERVICE_ID,
+            ASCS_UUID,
+            bt_gatt::types::ServiceKind::Primary,
+        );
+        for c in chars {
+            service_def.add_characteristic(c).unwrap();
+        }
+        Self {
+            service_def,
+            local_service: Default::default(),
+            default_client_endpoints,
+            client_endpoints: Default::default(),
+        }
+    }
+
+    fn build_control_point() -> Characteristic {
+        let properties = CharacteristicProperty::Write
+            | CharacteristicProperty::WriteWithoutResponse
+            | CharacteristicProperty::Notify;
+        let permissions =
+            AttributePermissions::with_levels(&properties, &SecurityLevels::encryption_required());
+        Characteristic {
+            handle: CONTROL_POINT_HANDLE,
+            uuid: Uuid::from_u16(0x2BC6),
+            properties,
+            permissions,
+            descriptors: Vec::new(),
+        }
+    }
+
+    pub fn publish(&mut self, server: &T::Server) -> Result<(), Error> {
+        if !self.local_service.is_not_published() {
+            return Err(Error::AlreadyPublished);
+        }
+        let LocalServiceState::NotPublished { waker } = std::mem::replace(
+            &mut self.local_service,
+            LocalServiceState::Preparing { fut: server.prepare(self.service_def.clone()) },
+        ) else {
+            unreachable!();
+        };
+        waker.map(Waker::wake);
+        Ok(())
+    }
+
+    pub fn release(&mut self, _id: AseId) -> Result<(), Error> {
+        unimplemented!()
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum AudioDirection {
+    Sink,
+    Source,
+}
+
+impl From<&AudioDirection> for bt_common::Uuid {
+    fn from(value: &AudioDirection) -> Self {
+        match value {
+            AudioDirection::Sink => Uuid::from_u16(0x2BC4),
+            AudioDirection::Source => Uuid::from_u16(0x2BC5),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+struct AudioStreamEndpoint {
+    handle: Handle,
+    direction: AudioDirection,
+    ase_id: AseId,
+    state: AseState,
+    // TODO(b/433287917): Add Additional Parameters for other states.
+    // Currently only works for Idle and Releasing states.
+}
+
+impl AudioStreamEndpoint {
+    fn into_char_value(&self) -> Vec<u8> {
+        let mut value = Vec::with_capacity(2);
+        value.push(self.ase_id.into());
+        value.push(self.state.into());
+        // TODO: add the additional_ase_parameters for the other states
+        value
+    }
+}
+
+impl From<&AudioStreamEndpoint> for Characteristic {
+    fn from(value: &AudioStreamEndpoint) -> Self {
+        let properties = CharacteristicProperty::Read | CharacteristicProperty::Notify;
+        let permissions =
+            AttributePermissions::with_levels(&properties, &SecurityLevels::encryption_required());
+        Characteristic {
+            handle: value.handle,
+            uuid: (&value.direction).into(),
+            properties,
+            permissions,
+            descriptors: Vec::new(),
+        }
+    }
+}
+
+struct ClientEndpoints {
+    endpoints: HashMap<AseId, AudioStreamEndpoint>,
+    handles: HashMap<Handle, AseId>,
+}
+
+impl ClientEndpoints {
+    fn new(source_count: u8, sink_count: u8) -> Self {
+        let dir_iter = std::iter::repeat(AudioDirection::Source)
+            .take(source_count as usize)
+            .chain(std::iter::repeat(AudioDirection::Sink).take(sink_count as usize));
+        // AseIds shall not have an id of 0
+        let (endpoints, handles) = (1..)
+            .zip(dir_iter)
+            .map(|(raw_ase_id, direction)| {
+                let handle = Handle(BASE_ENDPOINT_HANDLE.0 + raw_ase_id as u64);
+                let ase_id = AseId(raw_ase_id);
+                (
+                    (
+                        ase_id,
+                        AudioStreamEndpoint { handle, ase_id, direction, state: AseState::Idle },
+                    ),
+                    (handle, ase_id),
+                )
+            })
+            .unzip();
+        Self { endpoints, handles }
+    }
+
+    fn clone_for_peer(&self, _peer_id: PeerId) -> Self {
+        // TODO: Randomize the ASE_IDs.  Handles need to stay the same.
+        Self { endpoints: self.endpoints.clone(), handles: self.handles.clone() }
+    }
+}
+
+impl From<&ClientEndpoints> for Vec<Characteristic> {
+    fn from(value: &ClientEndpoints) -> Self {
+        value.endpoints.values().map(Into::into).collect()
+    }
+}
+
+pub enum ServiceEvent {}
+
+impl<T: bt_gatt::ServerTypes> Stream for AudioStreamControlServiceServer<T> {
+    type Item = Result<ServiceEvent, Error>;
+
+    fn poll_next(
+        self: std::pin::Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> std::task::Poll<Option<Self::Item>> {
+        let mut this = self.project();
+        loop {
+            let event = match futures::ready!(this.local_service.as_mut().poll_next(cx)) {
+                None => return Poll::Ready(None),
+                Some(Err(e)) => return Poll::Ready(Some(Err(e))),
+                Some(Ok(event)) => event,
+            };
+            use bt_gatt::server::ServiceEvent::*;
+            let peer_id = event.peer_id();
+            let peer_entry = this
+                .client_endpoints
+                .entry(peer_id)
+                .or_insert_with(|| this.default_client_endpoints.clone_for_peer(peer_id));
+            match event {
+                Read { handle, offset, responder, .. } => {
+                    let offset = offset as usize;
+                    if handle == CONTROL_POINT_HANDLE {
+                        responder.error(GattError::ReadNotPermitted);
+                        continue;
+                    }
+                    let Some(ase_id) = peer_entry.handles.get(&handle) else {
+                        responder.error(GattError::InvalidHandle);
+                        continue;
+                    };
+                    let Some(endpoint) = peer_entry.endpoints.get(ase_id) else {
+                        responder.error(GattError::UnlikelyError);
+                        continue;
+                    };
+                    let value = endpoint.into_char_value();
+                    if offset > value.len() {
+                        responder.error(GattError::InvalidOffset);
+                        continue;
+                    }
+                    responder.respond(&value[offset..]);
+                    continue;
+                }
+                Write { peer_id, handle, offset, value, responder } => {
+                    if handle != CONTROL_POINT_HANDLE {
+                        responder.error(GattError::WriteNotPermitted);
+                        continue;
+                    }
+                    if offset != 0 {
+                        // Offset write isn't allowed by the service?
+                        // TODO: determine if partial writes should be allowed
+                        responder.error(GattError::InvalidOffset);
+                        continue;
+                    }
+                    responder.acknowledge();
+                    let _op = match AseControlOperation::try_from(value.to_owned()) {
+                        Ok(op) => op,
+                        Err(e) => {
+                            let service_ref = this.local_service.as_ref();
+
+                            let value = e.notify_value();
+                            service_ref.service().unwrap().notify(
+                                &CONTROL_POINT_HANDLE,
+                                &value[..],
+                                &[peer_id],
+                            );
+                            continue;
+                        }
+                    };
+                    // TODO: Do the operation here, possibly notifying things
+                    continue;
+                }
+                ClientConfiguration { peer_id, handle, notification_type } => {
+                    log::info!(
+                        "ASCS Got ClientConfig for {peer_id:?}: {handle:?} {notification_type:?}"
+                    );
+                }
+                PeerInfo { peer_id, mtu, connected, .. } => {
+                    log::info!(
+                        "ASCS got PeerInfo {peer_id:?}: mtu {mtu:?}, connected: {connected:?}"
+                    );
+                }
+                _ => continue,
+            }
+        }
+    }
+}
+
+impl<T: bt_gatt::ServerTypes> FusedStream for AudioStreamControlServiceServer<T> {
+    fn is_terminated(&self) -> bool {
+        self.local_service.is_terminated()
+    }
+}
diff --git a/rust/bt-ascs/src/tests.rs b/rust/bt-ascs/src/tests.rs
new file mode 100644
index 0000000..3e88f87
--- /dev/null
+++ b/rust/bt-ascs/src/tests.rs
@@ -0,0 +1,143 @@
+// Copyright 2024 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use bt_common::{PeerId, Uuid};
+use bt_gatt::test_utils::{FakeServer, FakeServerEvent, FakeTypes};
+use bt_gatt::types::Handle;
+
+use futures::channel::mpsc::UnboundedReceiver;
+use futures::{Stream, StreamExt};
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+use crate::server::AudioStreamControlServiceServer;
+use crate::server::ServiceEvent;
+use crate::server::{ASCS_SERVICE_ID, ASCS_UUID};
+use crate::*;
+
+#[track_caller]
+fn expect_service_event(events: &mut UnboundedReceiver<FakeServerEvent>) -> FakeServerEvent {
+    match events.poll_next_unpin(&mut futures_test::task::noop_context()) {
+        Poll::Ready(Some(event)) => event,
+        x => panic!("Expected fake server event, got {x:?}"),
+    }
+}
+
+#[test]
+fn publishes() {
+    let mut ascs_server = std::pin::pin!(AudioStreamControlServiceServer::<FakeTypes>::new(1, 1));
+
+    let (count_waker, woken_count) = futures_test::task::new_count_waker();
+
+    // Polling the server before it's published should result in Poll::Pending
+    let poll_result = ascs_server.as_mut().poll_next(&mut Context::from_waker(&count_waker));
+
+    assert!(poll_result.is_pending());
+    assert_eq!(woken_count.get(), 0);
+
+    let (fake_server, mut events) = FakeServer::new();
+
+    let result = ascs_server.publish(&fake_server);
+    assert!(result.is_ok());
+
+    let already_published = ascs_server.publish(&fake_server);
+
+    assert!(already_published.is_err());
+
+    assert_eq!(woken_count.get(), 1);
+
+    let poll_result = ascs_server.poll_next(&mut Context::from_waker(&count_waker));
+
+    match expect_service_event(&mut events) {
+        FakeServerEvent::Published { id: _, definition } => {
+            assert_eq!(definition.uuid(), ASCS_UUID);
+            assert_eq!(
+                definition.characteristics().filter(|c| c.uuid == Uuid::from_u16(0x2BC4)).count(),
+                1
+            );
+            assert_eq!(
+                definition.characteristics().filter(|c| c.uuid == Uuid::from_u16(0x2BC5)).count(),
+                1
+            );
+        }
+        x => panic!("Expected published event, got {x:?}"),
+    };
+
+    // Should still be pending, even though we had some work to do.
+    assert!(poll_result.is_pending());
+}
+
+fn published_server() -> (
+    Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>,
+    FakeServer,
+    UnboundedReceiver<FakeServerEvent>,
+) {
+    let mut ascs_server = Box::pin(AudioStreamControlServiceServer::<FakeTypes>::new(1, 1));
+    let (fake_server, events) = FakeServer::new();
+
+    let result = ascs_server.publish(&fake_server);
+    assert!(result.is_ok());
+
+    assert!(ascs_server.poll_next_unpin(&mut futures_test::task::noop_context()).is_pending());
+
+    (ascs_server, fake_server, events)
+}
+
+fn poll_server(
+    server: &mut Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>,
+) -> Poll<Option<core::result::Result<ServiceEvent, Error>>> {
+    server.poll_next_unpin(&mut futures_test::task::noop_context())
+}
+
+// Ignored because we currently do nothing with operations.
+#[ignore]
+#[test]
+fn peers_are_separated() {
+    let (mut ascs_server, fake_server, mut server_events) = published_server();
+
+    // Read the sink uuid
+    fake_server.incoming_read(PeerId(1), ASCS_SERVICE_ID, Handle(2), 0);
+
+    // Poll the ascs server, should not result in an ASCS event
+    assert!(poll_server(&mut ascs_server).is_pending());
+
+    // Should have the response
+    let peer_one_value;
+    match server_events.poll_next_unpin(&mut futures_test::task::noop_context()) {
+        Poll::Ready(Some(FakeServerEvent::ReadResponded { service_id, handle: _, value })) => {
+            assert_eq!(service_id, ASCS_SERVICE_ID);
+            peer_one_value = value.unwrap();
+        }
+        x => panic!("Expected the read to be responded to got {x:?}"),
+    };
+
+    let ase_id = peer_one_value[0];
+
+    // Codec Configure the first peer ase_id
+    fake_server.incoming_write(
+        PeerId(1),
+        ASCS_SERVICE_ID,
+        Handle(1),
+        0,
+        vec![0x01, ase_id, 0x01, 0x01, 0x06, 0x00],
+    );
+
+    // Still shouldn't have any event
+    assert!(poll_server(&mut ascs_server).is_pending());
+
+    // Read the sink id from another peer
+    fake_server.incoming_read(PeerId(2), ASCS_SERVICE_ID, Handle(2), 0);
+
+    let peer_two_value;
+    match server_events.poll_next_unpin(&mut futures_test::task::noop_context()) {
+        Poll::Ready(Some(FakeServerEvent::ReadResponded { service_id, handle: _, value })) => {
+            assert_eq!(service_id, ASCS_SERVICE_ID);
+            peer_two_value = value.unwrap();
+        }
+        x => panic!("Expected the read to be responded to got {x:?}"),
+    };
+
+    let _ase_id = peer_two_value[0];
+    assert!(peer_one_value[1] != peer_two_value[1]);
+}
diff --git a/rust/bt-ascs/src/types.rs b/rust/bt-ascs/src/types.rs
new file mode 100644
index 0000000..2fb54ad
--- /dev/null
+++ b/rust/bt-ascs/src/types.rs
@@ -0,0 +1,991 @@
+// Copyright 2024 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use bt_common::packet_encoding::Decodable;
+use bt_common::{codable_as_bitmask, decodable_enum};
+use thiserror::Error;
+
+use bt_common::core::CodecId;
+use bt_common::generic_audio::metadata_ltv::Metadata;
+
+/// Error type
+#[derive(Debug, Error)]
+pub enum Error {
+    #[error("Reserved for Future Use: {0}")]
+    ReservedFutureUse(String),
+    #[error("Server Only Operation")]
+    ServerOnlyOperation,
+    #[error("Service is already published")]
+    AlreadyPublished,
+    #[error("Issue publishing service: {0}")]
+    PublishError(bt_gatt::types::Error),
+    #[error("Unsupported configuration: {0}")]
+    Unsupported(String),
+}
+
+#[non_exhaustive]
+#[derive(Debug, Clone, PartialEq)]
+pub enum ResponseCode {
+    Success { ase_id: AseId },
+    UnsupportedOpcode,
+    InvalidLength,
+    InvalidAseId { value: u8 },
+    InvalidAseStateMachineTransition { ase_id: AseId },
+    InvalidAseDirection { ase_id: AseId },
+    UnsupportedAudioCapablities { ase_id: AseId },
+    ConfigurationParameterValue { ase_id: AseId, issue: ResponseIssue, reason: ResponseReason },
+    Metadata { ase_id: AseId, issue: ResponseIssue, type_value: u8 },
+    InsufficientResources { ase_id: AseId },
+    UnspecifiedError { ase_id: AseId },
+}
+
+impl ResponseCode {
+    fn to_code(&self) -> u8 {
+        match self {
+            ResponseCode::Success { .. } => 0x00,
+            ResponseCode::UnsupportedOpcode => 0x01,
+            ResponseCode::InvalidLength => 0x02,
+            ResponseCode::InvalidAseId { .. } => 0x03,
+            ResponseCode::InvalidAseStateMachineTransition { .. } => 0x04,
+            ResponseCode::InvalidAseDirection { .. } => 0x05,
+            ResponseCode::UnsupportedAudioCapablities { .. } => 0x06,
+            ResponseCode::ConfigurationParameterValue {
+                issue: ResponseIssue::Unsupported, ..
+            } => 0x07,
+            ResponseCode::ConfigurationParameterValue {
+                issue: ResponseIssue::Rejected, ..
+            } => 0x08,
+            ResponseCode::ConfigurationParameterValue { issue: ResponseIssue::Invalid, .. } => 0x09,
+            ResponseCode::Metadata { issue: ResponseIssue::Unsupported, .. } => 0x0A,
+            ResponseCode::Metadata { issue: ResponseIssue::Rejected, .. } => 0x0B,
+            ResponseCode::Metadata { issue: ResponseIssue::Invalid, .. } => 0x0C,
+            ResponseCode::InsufficientResources { .. } => 0x0D,
+            ResponseCode::UnspecifiedError { .. } => 0x0E,
+        }
+    }
+
+    fn reason_byte(&self) -> u8 {
+        match self {
+            ResponseCode::ConfigurationParameterValue { reason, .. } => (*reason).into(),
+            ResponseCode::Metadata { type_value, .. } => *type_value,
+            _ => 0x00,
+        }
+    }
+
+    fn ase_id_value(&self) -> u8 {
+        match self {
+            ResponseCode::UnsupportedOpcode | ResponseCode::InvalidLength => 0x00,
+            ResponseCode::InvalidAseId { value } => *value,
+            ResponseCode::Success { ase_id }
+            | ResponseCode::InvalidAseStateMachineTransition { ase_id }
+            | ResponseCode::InvalidAseDirection { ase_id }
+            | ResponseCode::UnsupportedAudioCapablities { ase_id }
+            | ResponseCode::ConfigurationParameterValue { ase_id, .. }
+            | ResponseCode::Metadata { ase_id, .. }
+            | ResponseCode::InsufficientResources { ase_id }
+            | ResponseCode::UnspecifiedError { ase_id } => (*ase_id).into(),
+        }
+    }
+
+    pub(crate) fn notify_value(&self) -> Vec<u8> {
+        [self.ase_id_value(), self.to_code(), self.reason_byte()].into()
+    }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum ResponseIssue {
+    Unsupported,
+    Rejected,
+    Invalid,
+}
+
+decodable_enum! {
+#[non_exhaustive]
+pub enum ResponseReason<u8, bt_common::packet_encoding::Error, OutOfRange> {
+    CodecId = 0x01,
+    CodecSpecificConfiguration = 0x02,
+    SduInterval = 0x03,
+    Framing = 0x04,
+    Phy = 0x05,
+    MaximumSduSize = 0x06,
+    RetransmissionNumber = 0x07,
+    MaxTransportLatency = 0x08,
+    PresentationDelay = 0x09,
+    InvalidAseCisMapping = 0x0A,
+}
+}
+
+decodable_enum! {
+
+#[derive(Default)]
+pub enum AseState<u8, bt_common::packet_encoding::Error, OutOfRange> {
+    #[default]
+    Idle = 0x00,
+    CodecConfigured = 0x01,
+    QosConfigured = 0x02,
+    Enabling = 0x03,
+    Streaming = 0x04,
+    Disabling = 0x05,
+    Releasing = 0x06,
+}
+}
+
+/// Audio Stream Endpoint Identifier
+/// Exposed by the server in ASE characteristics
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct AseId(pub u8);
+
+impl AseId {
+    const BYTE_SIZE: usize = 1;
+}
+
+impl TryFrom<u8> for AseId {
+    type Error = ResponseCode;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        if value == 0 {
+            return Err(ResponseCode::InvalidAseId { value });
+        }
+        Ok(Self(value))
+    }
+}
+
+impl From<AseId> for u8 {
+    fn from(value: AseId) -> Self {
+        value.0
+    }
+}
+
+impl Decodable for AseId {
+    type Error = ResponseCode;
+
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        if buf.len() < 1 {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        (buf[0].try_into(), 1)
+    }
+}
+
+decodable_enum! {
+    pub enum AseControlPointOpcode<u8, ResponseCode, UnsupportedOpcode> {
+        ConfigCodec = 0x01,
+        ConfigQos = 0x02,
+        Enable = 0x03,
+        ReceiverStartReady = 0x04,
+        Disable = 0x05,
+        ReceiverStopReady = 0x06,
+        UpdateMetadata = 0x07,
+        Release = 0x08,
+    }
+}
+
+/// ASE Control Operations.  These can be initiated by a server or client.
+/// Defined in Table 4.6 of ASCS v1.0
+/// Marked non-exaustive as the remaining operations are RFU and new operations
+/// could arrive but should be rejected if they are not recognized.
+/// Some variants already contain responses as decoding errors are detected,
+/// i.e. invalid parameters or metadata, which will be delivered after the
+/// operation is complete with the results from the rest of the operation.
+#[non_exhaustive]
+#[derive(Debug, PartialEq, Clone)]
+pub enum AseControlOperation {
+    ConfigCodec { codec_configurations: Vec<CodecConfiguration>, responses: Vec<ResponseCode> },
+    ConfigQos { qos_configurations: Vec<QosConfiguration>, responses: Vec<ResponseCode> },
+    Enable { ases_with_metadata: Vec<AseIdWithMetadata>, responses: Vec<ResponseCode> },
+    ReceiverStartReady { ases: Vec<AseId> },
+    // The only possible error here is InvalidLength
+    Disable { ases: Vec<AseId> },
+    // The only possible error here is InvalidLength
+    ReceiverStopReady { ases: Vec<AseId> },
+    UpdateMetadata { ases_with_metadata: Vec<AseIdWithMetadata>, responses: Vec<ResponseCode> },
+    // The only possible error here is InvalidLength
+    Release { ases: Vec<AseId> },
+    // This is only initiated by the server
+    Released,
+}
+
+impl AseControlOperation {
+    const MIN_BYTE_SIZE: usize = 3;
+
+    fn contains_invalid_length(&self) -> bool {
+        match self {
+            Self::ConfigCodec { responses, .. }
+            | Self::ConfigQos { responses, .. }
+            | Self::Enable { responses, .. }
+            | Self::UpdateMetadata { responses, .. } => {
+                responses.contains(&ResponseCode::InvalidLength)
+            }
+            _ => false,
+        }
+    }
+}
+
+impl TryFrom<AseControlOperation> for u8 {
+    type Error = Error;
+
+    fn try_from(value: AseControlOperation) -> Result<Self, Self::Error> {
+        match value {
+            AseControlOperation::ConfigCodec { .. } => Ok(0x01),
+            AseControlOperation::ConfigQos { .. } => Ok(0x02),
+            AseControlOperation::Enable { .. } => Ok(0x03),
+            AseControlOperation::ReceiverStartReady { .. } => Ok(0x04),
+            AseControlOperation::Disable { .. } => Ok(0x05),
+            AseControlOperation::ReceiverStopReady { .. } => Ok(0x06),
+            AseControlOperation::UpdateMetadata { .. } => Ok(0x07),
+            AseControlOperation::Release { .. } => Ok(0x08),
+            AseControlOperation::Released => Err(Error::ServerOnlyOperation),
+        }
+    }
+}
+
+fn partition_results<T, E>(collection: Vec<Result<T, E>>) -> (Vec<T>, Vec<E>) {
+    let mut oks = Vec::with_capacity(collection.len());
+    let mut errs = Vec::with_capacity(collection.len());
+    for item in collection {
+        match item {
+            Ok(x) => oks.push(x),
+            Err(e) => errs.push(e),
+        }
+    }
+    (oks, errs)
+}
+
+impl TryFrom<Vec<u8>> for AseControlOperation {
+    type Error = ResponseCode;
+
+    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+        if value.len() < Self::MIN_BYTE_SIZE {
+            return Err(ResponseCode::InvalidLength);
+        }
+        let operation: AseControlPointOpcode = value[0].try_into()?;
+        let number_of_ases = value[1] as usize;
+        if number_of_ases < 1 {
+            return Err(ResponseCode::InvalidLength);
+        }
+        let (op, consumed) = match operation {
+            AseControlPointOpcode::ConfigCodec => {
+                let (results, consumed) =
+                    CodecConfiguration::decode_multiple(&value[2..], Some(number_of_ases));
+                let (codec_configurations, responses) = partition_results(results);
+                (Self::ConfigCodec { codec_configurations, responses }, consumed)
+            }
+            AseControlPointOpcode::ConfigQos => {
+                let (results, consumed) =
+                    QosConfiguration::decode_multiple(&value[2..], Some(number_of_ases));
+                let (qos_configurations, responses) = partition_results(results);
+                (Self::ConfigQos { qos_configurations, responses }, consumed)
+            }
+            AseControlPointOpcode::Enable => {
+                let (results, consumed) =
+                    AseIdWithMetadata::decode_multiple(&value[2..], Some(number_of_ases));
+                let (ases_with_metadata, responses) = partition_results(results);
+                (Self::Enable { ases_with_metadata, responses }, consumed)
+            }
+            AseControlPointOpcode::ReceiverStartReady => {
+                // Only InvalidLength is possible
+                let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases));
+                let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?;
+                (Self::ReceiverStartReady { ases }, consumed)
+            }
+            AseControlPointOpcode::Disable => {
+                // Only InvalidLength is possible
+                let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases));
+                let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?;
+                (Self::Disable { ases }, consumed)
+            }
+            AseControlPointOpcode::ReceiverStopReady => {
+                // Only InvalidLength is possible
+                let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases));
+                let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?;
+                (Self::ReceiverStopReady { ases }, consumed)
+            }
+            AseControlPointOpcode::UpdateMetadata => {
+                let (results, consumed) =
+                    AseIdWithMetadata::decode_multiple(&value[2..], Some(number_of_ases));
+                let (ases_with_metadata, responses) = partition_results(results);
+                (Self::UpdateMetadata { ases_with_metadata, responses }, consumed)
+            }
+            AseControlPointOpcode::Release => {
+                // Only InvalidLength is possible
+                let (results, consumed) = AseId::decode_multiple(&value[2..], Some(number_of_ases));
+                let ases = results.into_iter().collect::<Result<Vec<_>, ResponseCode>>()?;
+                (Self::Release { ases }, consumed)
+            }
+        };
+        // A client-initiated ASE Control operation shall also be defined as an invalid
+        // length operation if the total length of all parameters written by the
+        // client is not equal to the total length of all fixed parameters plus
+        // the length of any variable length parameters for that operation as
+        // defined in Section 5.1 through Section 5.8.
+        if (consumed + 2) != value.len() {
+            return Err(ResponseCode::InvalidLength);
+        }
+        if op.contains_invalid_length() {
+            return Err(ResponseCode::InvalidLength);
+        }
+        Ok(op)
+    }
+}
+
+decodable_enum! {
+pub enum TargetLatency<u8, bt_common::packet_encoding::Error, OutOfRange> {
+    TargetLowLatency = 0x01,
+    TargetBalanced = 0x02,
+    TargetHighReliability = 0x03,
+}
+}
+
+impl TargetLatency {
+    const BYTE_SIZE: usize = 1;
+}
+
+decodable_enum! {
+pub enum TargetPhy<u8, bt_common::packet_encoding::Error, OutOfRange> {
+    Le1MPhy = 0x01,
+    Le2MPhy = 0x02,
+    LeCodedPhy = 0x03,
+}
+}
+
+impl TargetPhy {
+    const BYTE_SIZE: usize = 1;
+}
+
+decodable_enum! {
+    pub enum Phy<u8, bt_common::packet_encoding::Error, OutOfRange> {
+        Le1MPhy = 0b0001,
+        Le2MPhy = 0b0010,
+        LeCodedPhy = 0b0100,
+    }
+}
+
+codable_as_bitmask!(Phy, u8);
+
+impl Phy {
+    const BYTE_SIZE: usize = 1;
+}
+
+/// Represents Config Codec parameters for a single ASE. See ASCS v1.0.1 Section
+/// 5.2.
+#[derive(Debug, Clone, PartialEq)]
+pub struct CodecConfiguration {
+    pub ase_id: AseId,
+    pub target_latency: TargetLatency,
+    pub target_phy: TargetPhy,
+    pub codec_id: CodecId,
+    pub codec_specific_configuration: Vec<u8>,
+}
+
+impl CodecConfiguration {
+    const MIN_BYTE_SIZE: usize =
+        AseId::BYTE_SIZE + TargetLatency::BYTE_SIZE + TargetPhy::BYTE_SIZE + CodecId::BYTE_SIZE + 1;
+}
+
+impl Decodable for CodecConfiguration {
+    type Error = ResponseCode;
+
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        if buf.len() < Self::MIN_BYTE_SIZE {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        let codec_specific_configuration_len = buf[Self::MIN_BYTE_SIZE - 1] as usize;
+        let total_len = codec_specific_configuration_len + Self::MIN_BYTE_SIZE;
+        if buf.len() < total_len {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        let try_decode_fn = |buf: &[u8]| {
+            let ase_id = AseId::try_from(buf[0])?;
+            let Ok(target_latency) = TargetLatency::try_from(buf[1]) else {
+                // TODO: unclear what to do if the target latency is out of range.
+                return Err(ResponseCode::ConfigurationParameterValue {
+                    ase_id,
+                    issue: ResponseIssue::Invalid,
+                    reason: ResponseReason::MaxTransportLatency,
+                });
+            };
+            let Ok(target_phy) = TargetPhy::try_from(buf[2]) else {
+                return Err(ResponseCode::ConfigurationParameterValue {
+                    ase_id,
+                    issue: ResponseIssue::Unsupported,
+                    reason: ResponseReason::Phy,
+                });
+            };
+            let Ok(codec_id) = CodecId::decode(&buf[3..]).0 else {
+                return Err(ResponseCode::ConfigurationParameterValue {
+                    ase_id,
+                    issue: ResponseIssue::Invalid,
+                    reason: ResponseReason::CodecId,
+                });
+            };
+            let codec_specific_configuration = Vec::from(
+                &buf[Self::MIN_BYTE_SIZE..Self::MIN_BYTE_SIZE + codec_specific_configuration_len],
+            );
+            Ok(Self { ase_id, target_latency, target_phy, codec_id, codec_specific_configuration })
+        };
+        (try_decode_fn(buf), total_len)
+    }
+}
+
+/// Represents Config QoS parameters for a single ASE. See ASCS v1.0.1 Section
+/// 5.2.
+#[derive(Debug, Clone, PartialEq)]
+pub struct QosConfiguration {
+    pub ase_id: AseId,
+    pub cig_id: CigId,
+    pub cis_id: CisId,
+    pub sdu_interval: SduInterval,
+    pub framing: Framing,
+    pub phy: Vec<Phy>,
+    pub max_sdu: MaxSdu,
+    pub retransmission_number: u8,
+    pub max_transport_latency: MaxTransportLatency,
+    pub presentation_delay: PresentationDelay,
+}
+
+impl QosConfiguration {
+    const BYTE_SIZE: usize = AseId::BYTE_SIZE
+        + CigId::BYTE_SIZE
+        + CisId::BYTE_SIZE
+        + SduInterval::BYTE_SIZE
+        + Framing::BYTE_SIZE
+        + Phy::BYTE_SIZE
+        + MaxSdu::BYTE_SIZE
+        + 1 // retransmission_number
+        + MaxTransportLatency::BYTE_SIZE
+        + PresentationDelay::BYTE_SIZE;
+}
+
+impl Decodable for QosConfiguration {
+    type Error = ResponseCode;
+
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        if buf.len() < QosConfiguration::BYTE_SIZE {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        let try_decode_fn = |buf: &[u8]| {
+            let ase_id = AseId::try_from(buf[0])?;
+            let cig_id =
+                CigId::try_from(buf[1]).map_err(|_e| ResponseCode::UnspecifiedError { ase_id })?;
+            let cis_id =
+                CisId::try_from(buf[2]).map_err(|_e| ResponseCode::UnspecifiedError { ase_id })?;
+            let sdu_interval;
+            match SduInterval::decode(&buf[3..]) {
+                (Ok(interval), _) => {
+                    sdu_interval = interval;
+                }
+                (Err(bt_common::packet_encoding::Error::BufferTooSmall), _) => {
+                    return Err(ResponseCode::InvalidLength);
+                }
+                (Err(bt_common::packet_encoding::Error::OutOfRange), _) => {
+                    return Err(ResponseCode::ConfigurationParameterValue {
+                        ase_id,
+                        issue: ResponseIssue::Invalid,
+                        reason: ResponseReason::SduInterval,
+                    });
+                }
+                _ => unreachable!(),
+            };
+            let Ok(framing) = Framing::try_from(buf[6]) else {
+                return Err(ResponseCode::ConfigurationParameterValue {
+                    ase_id,
+                    issue: ResponseIssue::Invalid,
+                    reason: ResponseReason::Framing,
+                });
+            };
+            let phy = Phy::from_bits(buf[7]).collect();
+            let max_sdu = [buf[8], buf[9]].try_into().map_err(|_e| {
+                ResponseCode::ConfigurationParameterValue {
+                    issue: ResponseIssue::Invalid,
+                    reason: ResponseReason::MaximumSduSize,
+                    ase_id,
+                }
+            })?;
+            let retransmission_number = buf[10];
+            let max_transport_latency =
+                MaxTransportLatency::decode(&buf[11..]).0.map_err(|e| match e {
+                    bt_common::packet_encoding::Error::BufferTooSmall => {
+                        ResponseCode::InvalidLength
+                    }
+                    bt_common::packet_encoding::Error::OutOfRange => {
+                        ResponseCode::ConfigurationParameterValue {
+                            issue: ResponseIssue::Invalid,
+                            reason: ResponseReason::MaxTransportLatency,
+                            ase_id,
+                        }
+                    }
+                    _ => unreachable!(),
+                })?;
+            let presentation_delay = PresentationDelay::decode(&buf[13..]).0?;
+            Ok(Self {
+                ase_id,
+                cig_id,
+                cis_id,
+                sdu_interval,
+                framing,
+                phy,
+                max_sdu,
+                retransmission_number,
+                max_transport_latency,
+                presentation_delay,
+            })
+        };
+        (try_decode_fn(buf), QosConfiguration::BYTE_SIZE)
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct CigId(u8);
+
+impl CigId {
+    const BYTE_SIZE: usize = 1;
+}
+
+impl TryFrom<u8> for CigId {
+    type Error = bt_common::packet_encoding::Error;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        if value > 0xEF { Err(Self::Error::OutOfRange) } else { Ok(CigId(value)) }
+    }
+}
+
+impl From<CigId> for u8 {
+    fn from(value: CigId) -> Self {
+        value.0
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct CisId(u8);
+
+impl CisId {
+    const BYTE_SIZE: usize = 1;
+}
+
+impl TryFrom<u8> for CisId {
+    type Error = bt_common::packet_encoding::Error;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        if value > 0xEF { Err(Self::Error::OutOfRange) } else { Ok(CisId(value)) }
+    }
+}
+
+impl From<CisId> for u8 {
+    fn from(value: CisId) -> Self {
+        value.0
+    }
+}
+
+/// SDU Inteval parameter
+/// This value is 24 bits long and little-endian on the wire.
+/// It is stored native-endian here.
+/// Valid range is [0x0000FF, 0x0FFFFF].
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub struct SduInterval(u32);
+
+impl SduInterval {
+    const BYTE_SIZE: usize = 3;
+}
+
+impl Decodable for SduInterval {
+    type Error = bt_common::packet_encoding::Error;
+
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        if buf.len() < Self::BYTE_SIZE {
+            return (Err(Self::Error::BufferTooSmall), buf.len());
+        }
+        let val = u32::from_le_bytes([buf[0], buf[1], buf[2], 0]);
+        if val < 0xFF || val > 0x0FFFFF {
+            return (Err(Self::Error::OutOfRange), Self::BYTE_SIZE);
+        }
+        (Ok(SduInterval(val)), Self::BYTE_SIZE)
+    }
+}
+
+decodable_enum! {
+pub enum Framing<u8, bt_common::packet_encoding::Error, OutOfRange> {
+    Unframed = 0x00,
+    Framed = 0x01,
+}
+}
+
+impl Framing {
+    const BYTE_SIZE: usize = 1;
+}
+
+/// Max SDU parameter value.
+/// Valid range is 0x0000-0x0FFF
+/// Transmitted in little-endian. Stored here in native-endian.
+#[derive(Debug, Clone, PartialEq)]
+pub struct MaxSdu(u16);
+
+impl TryFrom<[u8; 2]> for MaxSdu {
+    type Error = bt_common::packet_encoding::Error;
+
+    fn try_from(value: [u8; 2]) -> Result<Self, Self::Error> {
+        let value = u16::from_le_bytes([value[0], value[1]]);
+        if value > 0xFFF {
+            return Err(Self::Error::OutOfRange);
+        }
+        Ok(MaxSdu(value))
+    }
+}
+
+impl MaxSdu {
+    const BYTE_SIZE: usize = 2;
+}
+
+/// Max Transport Latency
+/// Valid range is [0x0005, 0x0FA0].
+/// Transmitted in little-endian, Stored in native-endian.
+#[derive(Debug, Clone, PartialEq)]
+pub struct MaxTransportLatency(u16);
+
+impl Decodable for MaxTransportLatency {
+    type Error = bt_common::packet_encoding::Error;
+
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        if buf.len() < Self::BYTE_SIZE {
+            return (Err(Self::Error::BufferTooSmall), buf.len());
+        }
+        let val = u16::from_le_bytes([buf[0], buf[1]]);
+        if val < 0x0005 || val > 0x0FA0 {
+            return (Err(Self::Error::OutOfRange), Self::BYTE_SIZE);
+        }
+        (Ok(MaxTransportLatency(val)), Self::BYTE_SIZE)
+    }
+}
+
+impl TryFrom<std::time::Duration> for MaxTransportLatency {
+    type Error = bt_common::packet_encoding::Error;
+
+    fn try_from(value: std::time::Duration) -> Result<Self, Self::Error> {
+        let Ok(milliseconds) = u16::try_from(value.as_millis()) else {
+            return Err(Self::Error::OutOfRange);
+        };
+        if !(0x0005..=0x0FA0).contains(&milliseconds) {
+            return Err(Self::Error::OutOfRange);
+        }
+        Ok(Self(milliseconds))
+    }
+}
+
+impl MaxTransportLatency {
+    const BYTE_SIZE: usize = 2;
+}
+
+/// Presentation delay parameter value being requested by the client for an ASE.
+/// This value is 24 bits long (0x00FFFFFF max)
+/// Transmitted in little-endian, Stored in native-endian.
+#[derive(Debug, Clone, PartialEq)]
+pub struct PresentationDelay {
+    pub microseconds: u32,
+}
+
+impl PresentationDelay {
+    const BYTE_SIZE: usize = 3;
+}
+
+impl Decodable for PresentationDelay {
+    type Error = ResponseCode;
+
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        if buf.len() < Self::BYTE_SIZE {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        let microseconds = u32::from_le_bytes([buf[0], buf[1], buf[2], 0]);
+        (Ok(PresentationDelay { microseconds }), Self::BYTE_SIZE)
+    }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct AseIdWithMetadata {
+    pub ase_id: AseId,
+    pub metadata: Vec<Metadata>,
+}
+
+impl AseIdWithMetadata {
+    const MIN_BYTE_SIZE: usize = 2;
+}
+
+impl Decodable for AseIdWithMetadata {
+    type Error = ResponseCode;
+
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        if buf.len() < Self::MIN_BYTE_SIZE {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        let ase_id = match AseId::try_from(buf[0]) {
+            Ok(ase_id) => ase_id,
+            Err(e) => return (Err(e), buf.len()),
+        };
+        let metadata_length = buf[1] as usize;
+        let total_length = Self::MIN_BYTE_SIZE + metadata_length;
+        if buf.len() < total_length {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        use bt_common::core::ltv::Error as LtvError;
+        use bt_common::core::ltv::LtValue;
+        let (metadata_results, consumed) = Metadata::decode_all(&buf[2..2 + metadata_length]);
+        if consumed != metadata_length {
+            return (Err(ResponseCode::InvalidLength), buf.len());
+        }
+        let metadata_result: Result<Vec<Metadata>, LtvError<<Metadata as LtValue>::Type>> =
+            metadata_results.into_iter().collect();
+        let Ok(metadata) = metadata_result else {
+            match metadata_result.unwrap_err() {
+                LtvError::MissingType => return (Err(ResponseCode::InvalidLength), buf.len()),
+                LtvError::MissingData(_) => return (Err(ResponseCode::InvalidLength), buf.len()),
+                LtvError::UnrecognizedType(_, type_value) => {
+                    return (
+                        Err(ResponseCode::Metadata {
+                            ase_id,
+                            issue: ResponseIssue::Unsupported,
+                            type_value,
+                        }),
+                        total_length,
+                    );
+                }
+                LtvError::LengthOutOfRange(_, t, _) | LtvError::TypeFailedToDecode(t, _) => {
+                    return (
+                        Err(ResponseCode::Metadata {
+                            ase_id,
+                            issue: ResponseIssue::Invalid,
+                            type_value: t.into(),
+                        }),
+                        total_length,
+                    );
+                }
+            }
+        };
+        (Ok(Self { ase_id, metadata }), total_length)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use bt_common::packet_encoding::Encodable;
+
+    use bt_common::core::ltv::LtValue;
+    use bt_common::generic_audio::{AudioLocation, codec_configuration};
+
+    #[test]
+    fn codec_configuration_roundtrip() {
+        let codec_specific_configuration = vec![
+            codec_configuration::CodecConfiguration::SamplingFrequency(
+                codec_configuration::SamplingFrequency::F48000Hz,
+            ),
+            codec_configuration::CodecConfiguration::FrameDuration(
+                codec_configuration::FrameDuration::TenMs,
+            ),
+            codec_configuration::CodecConfiguration::AudioChannelAllocation(
+                [AudioLocation::FrontLeft].into_iter().collect(),
+            ),
+            codec_configuration::CodecConfiguration::CodecFramesPerSdu(1),
+        ];
+        let codec_config_len =
+            codec_specific_configuration.iter().fold(0, |a, x| a + x.encoded_len());
+        let mut vec = Vec::with_capacity(codec_config_len);
+        vec.resize(codec_config_len, 0);
+        LtValue::encode_all(codec_specific_configuration.clone().into_iter(), &mut vec[..])
+            .unwrap();
+
+        let _codec_config = CodecConfiguration {
+            ase_id: AseId(5),
+            target_latency: TargetLatency::TargetLowLatency,
+            target_phy: TargetPhy::Le1MPhy,
+            codec_id: CodecId::Assigned(bt_common::core::CodingFormat::Lc3),
+            codec_specific_configuration: vec,
+        };
+    }
+
+    #[test]
+    fn codec_configuration_decode() {
+        let encoded = &[
+            0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08, 0x02, 0x02,
+            0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00,
+        ];
+
+        let (codec_config, size) = CodecConfiguration::decode(&encoded[..]);
+        let codec_config = codec_config.unwrap();
+        assert_eq!(size, encoded.len());
+        assert_eq!(codec_config.ase_id, AseId(4));
+        assert_eq!(codec_config.target_latency, TargetLatency::TargetBalanced);
+        assert_eq!(codec_config.target_phy, TargetPhy::Le2MPhy);
+        assert_eq!(codec_config.codec_id, CodecId::Assigned(bt_common::core::CodingFormat::Lc3));
+        assert_eq!(codec_config.codec_specific_configuration.len(), 0x10);
+    }
+
+    #[test]
+    fn ase_control_operation_decode_config_codec() {
+        let encoded = &[
+            0x01, 0x01, 0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08,
+            0x02, 0x02, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00,
+        ];
+
+        let codec_config = CodecConfiguration::decode(&encoded[2..]).0.unwrap();
+        let encoded_vec: Vec<u8> = encoded.into_iter().copied().collect();
+        let operation = AseControlOperation::try_from(encoded_vec);
+
+        let Ok(operation) = operation else {
+            panic!("Expected decode to work correctly, got {operation:?}");
+        };
+
+        assert_eq!(
+            operation,
+            AseControlOperation::ConfigCodec {
+                codec_configurations: vec![codec_config],
+                responses: Vec::new()
+            }
+        );
+    }
+
+    #[test]
+    fn ase_control_operation_decode_config_codec_some_failures() {
+        #[rustfmt::skip]
+        let encoded = &[
+            0x01, 0x02, // Config Codec, Two ASE_IDs
+            // First ASE_ID config
+            0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08,
+            0x02, 0x02, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00,
+            // Second ASE_ID config, fails based on invalid parameter value
+            0x05, 0x05, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        ];
+
+        let codec_config = CodecConfiguration::decode(&encoded[2..]).0.unwrap();
+        let encoded_vec: Vec<u8> = encoded.into_iter().copied().collect();
+        let operation = AseControlOperation::try_from(encoded_vec);
+
+        let Ok(operation) = operation else {
+            panic!("Expected decode to work correctly, got {operation:?}");
+        };
+
+        match operation {
+            AseControlOperation::ConfigCodec { codec_configurations, responses } => {
+                assert_eq!(codec_configurations, vec![codec_config]);
+                assert_eq!(responses.len(), 1);
+                assert!(matches!(responses[0], ResponseCode::ConfigurationParameterValue { .. }));
+            }
+            x => panic!("Expected ConfigCodec, got {x:?}"),
+        };
+    }
+
+    #[test]
+    fn ase_control_operation_decode_invalid_length() {
+        #[rustfmt::skip]
+        let encoded = &[
+            0x01, 0x02, // Config Codec, Two ASE_IDs
+            // First ASE_ID config
+            0x04, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x01, 0x08,
+            0x02, 0x02, 0x01, 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x04, 0x78, 0x00,
+            // Second ASE_ID config, fails based on invalid length
+            0x05, 0x05, 0x02, 0x06, 0x00, 0x00, 0x00,
+        ];
+
+        let encoded_vec: Vec<u8> = encoded.into_iter().copied().collect();
+        let operation = AseControlOperation::try_from(encoded_vec);
+
+        assert_eq!(operation, Err(ResponseCode::InvalidLength));
+    }
+
+    #[test]
+    fn qos_configuration_decode() {
+        let encoded = &[
+            0x03, // ASE_ID,
+            0x00, // CIG_ID,
+            0x00, // CIS_ID,
+            0x10, 0x27, 0x00, 0x01, 0x02, 0x28, 0x00, 0x02, 0xf4, 0x01, 0x18, 0x00, 0x80,
+        ];
+        let (qos_config, consumed) = QosConfiguration::decode(&encoded[..]);
+
+        let qos_config = qos_config.unwrap();
+
+        assert_eq!(consumed, encoded.len());
+        assert_eq!(qos_config.ase_id, AseId(3));
+        assert_eq!(qos_config.cig_id, CigId(0));
+        assert_eq!(qos_config.cis_id, CisId(0));
+        assert_eq!(qos_config.sdu_interval, SduInterval(10000));
+        assert_eq!(qos_config.framing, Framing::Framed);
+        assert_eq!(qos_config.phy, vec![Phy::Le2MPhy]);
+        assert_eq!(qos_config.max_sdu, MaxSdu(40));
+        assert_eq!(qos_config.retransmission_number, 2);
+        assert_eq!(qos_config.max_transport_latency, MaxTransportLatency(500));
+        assert_eq!(qos_config.presentation_delay, PresentationDelay { microseconds: 8388632 });
+    }
+
+    #[test]
+    fn aseid_with_metadata_decode() {
+        let encoded = &[
+            0x03, // ASE_ID
+            0x08, // metadata length
+            0x04, 0x04, 0x65, 0x6E, 0x67, // Language code
+            0x02, 0x03, 0x61, // Program Info
+        ];
+
+        let (ase_with_metadata, consumed) = AseIdWithMetadata::decode(&encoded[..]);
+        let ase_with_metadata = ase_with_metadata.unwrap();
+        assert_eq!(consumed, encoded.len());
+        assert_eq!(ase_with_metadata.ase_id, AseId(3));
+        assert_eq!(ase_with_metadata.metadata.len(), 2);
+    }
+
+    #[test]
+    fn ase_control_operation_decode_qos() {
+        #[rustfmt::skip]
+        let encoded_qos = &[
+            0x02, 0x01, // Qos OpCode, 1 number_of_ases
+            0x03, // ASE_ID,
+            0x00, // CIG_ID,
+            0x00, // CIS_ID,
+            0x10, 0x27, 0x00, // SduInterval
+            0x01, 0x02, 0x28, 0x00, 0x02, 0xf4,
+            0x01, 0x18, 0x00, 0x80,
+        ];
+        assert_eq!(QosConfiguration::BYTE_SIZE, encoded_qos.len() - 2);
+        let (qos_config, consumed) = QosConfiguration::decode(&encoded_qos[2..]);
+        assert!(qos_config.is_ok());
+        assert_eq!(consumed, encoded_qos.len() - 2);
+        let encoded_qos: Vec<u8> = encoded_qos.into_iter().copied().collect();
+        let operation = AseControlOperation::try_from(encoded_qos);
+        let Ok(_operation) = operation else {
+            panic!("Expected decode to work correctly, for {operation:?}");
+        };
+
+        #[rustfmt::skip]
+        let encoded_qos_one_fails = &[
+            0x02, 0x03, // Qos OpCode, 3 number_of_ases
+            0x03, // ASE_ID,
+            0x00, // CIG_ID,
+            0x00, // CIS_ID,
+            0x10, 0x27, 0x00, // SduInterval
+            0x01, 0x02, 0x28, 0x00, 0x02, 0xf4,
+            0x01, 0x18, 0x00, 0x80,
+            0x00, // ASE_ID, (AseId 0 is not allowed)
+            0x00, // CIG_ID,
+            0x00, // CIS_ID,
+            0x10, 0x27, 0x00, // SduInterval
+            0x01, 0x02, 0x28, 0x00, 0x02, 0xf4,
+            0x01, 0x18, 0x00, 0x80,
+            0x04, // ASE_ID
+            0x00, // CIG_ID,
+            0x00, // CIS_ID,
+            0x10, 0x17, 0x00, // SduInterval
+            0x01, 0x02, 0x28, 0x00, 0x02, 0xf4,
+            0x01, 0x18, 0x00, 0x80,
+        ];
+
+        let encoded_qos_one_fails: Vec<u8> = encoded_qos_one_fails.into_iter().copied().collect();
+
+        match AseControlOperation::try_from(encoded_qos_one_fails) {
+            Ok(AseControlOperation::ConfigQos { qos_configurations, responses }) => {
+                assert_eq!(qos_configurations.len(), 2);
+                assert_eq!(responses.len(), 1);
+                assert_eq!(responses[0], ResponseCode::InvalidAseId { value: 0x00 });
+            }
+            x => panic!("Expected ConfigQos to succeed, got {x:?}"),
+        };
+    }
+}
diff --git a/rust/bt-bap/src/types.rs b/rust/bt-bap/src/types.rs
index cdb3c7e..a97a657 100644
--- a/rust/bt-bap/src/types.rs
+++ b/rust/bt-bap/src/types.rs
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use bt_common::core::ltv::LtValue;
 use bt_common::core::CodecId;
+use bt_common::core::ltv::LtValue;
 use bt_common::generic_audio::codec_configuration::CodecConfiguration;
 use bt_common::generic_audio::metadata_ltv::Metadata;
 use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError};
@@ -51,13 +51,13 @@
 impl Decodable for BroadcastId {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() != Self::BYTE_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
         let padded_bytes = [buf[0], buf[1], buf[2], 0x00];
-        Ok((BroadcastId(u32::from_le_bytes(padded_bytes)), Self::BYTE_SIZE))
+        (Ok(BroadcastId(u32::from_le_bytes(padded_bytes))), Self::BYTE_SIZE)
     }
 }
 
@@ -96,17 +96,21 @@
 impl Decodable for BroadcastAudioAnnouncement {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < Self::PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
-        let (broadcast_id, _) = BroadcastId::decode(&buf[0..3])?;
-        // According to the spec, broadcast audio announcement service data inlcudes
-        // broadcast id and any additional service data. We don't store any
-        // additional parameters, so for now we just "consume" all of data buffer
-        // without doing anything.
-        Ok((Self { broadcast_id }, buf.len()))
+        match BroadcastId::decode(&buf[0..3]) {
+            (Ok(broadcast_id), _) => {
+                // According to the spec, broadcast audio announcement service data inlcudes
+                // broadcast id and any additional service data. We don't store any
+                // additional parameters, so for now we just "consume" all of data buffer
+                // without doing anything.
+                (Ok(Self { broadcast_id }), buf.len())
+            }
+            (Err(e), _) => (Err(e), buf.len()),
+        }
     }
 }
 
@@ -128,9 +132,9 @@
 impl Decodable for BroadcastAudioSourceEndpoint {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < Self::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
         let mut idx = 0 as usize;
@@ -140,20 +144,27 @@
         let num_big: usize = buf[idx] as usize;
         idx += 1;
         if num_big < 1 {
-            return Err(PacketError::InvalidParameter(format!(
-                "num of subgroups shall be at least 1 got {num_big}"
-            )));
+            return (
+                Err(PacketError::InvalidParameter(format!(
+                    "num of subgroups shall be at least 1 got {num_big}"
+                ))),
+                buf.len(),
+            );
         }
-
         let mut big = Vec::new();
         while big.len() < num_big {
-            let (group, len) = BroadcastIsochronousGroup::decode(&buf[idx..])
-                .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
-            big.push(group);
-            idx += len;
+            match BroadcastIsochronousGroup::decode(&buf[idx..]) {
+                (Ok(group), consumed) => {
+                    big.push(group);
+                    idx += consumed;
+                }
+                (Err(e), _) => {
+                    return (Err(PacketError::InvalidParameter(e.to_string())), buf.len());
+                }
+            };
         }
 
-        Ok((Self { presentation_delay_ms: presentation_delay, big }, idx))
+        (Ok(Self { presentation_delay_ms: presentation_delay, big }), idx)
     }
 }
 
@@ -178,59 +189,83 @@
 impl Decodable for BroadcastIsochronousGroup {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < BroadcastIsochronousGroup::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
         let mut idx = 0;
         let num_bis = buf[idx] as usize;
         idx += 1;
         if num_bis < 1 {
-            return Err(PacketError::InvalidParameter(format!(
-                "num of BIS shall be at least 1 got {num_bis}"
-            )));
+            return (
+                Err(PacketError::InvalidParameter(format!(
+                    "num of BIS shall be at least 1 got {num_bis}"
+                ))),
+                buf.len(),
+            );
         }
 
-        let (codec_id, read_bytes) = CodecId::decode(&buf[idx..])?;
-        idx += read_bytes;
+        let mut decode_fn = || {
+            let codec_id;
+            match CodecId::decode(&buf[idx..]) {
+                (Ok(id), consumed) => {
+                    codec_id = id;
+                    idx += consumed;
+                }
+                (Err(e), _) => {
+                    return Err(e);
+                }
+            };
 
-        let codec_config_len = buf[idx] as usize;
-        idx += 1;
-        if idx + codec_config_len > buf.len() {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            let codec_config_len = buf[idx] as usize;
+            idx += 1;
+            if idx + codec_config_len > buf.len() {
+                return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            }
+            let (results, consumed) =
+                CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]);
+            if consumed != codec_config_len {
+                return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            }
+
+            let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect();
+            idx += codec_config_len;
+
+            let metadata_len = buf[idx] as usize;
+            idx += 1;
+            if idx + metadata_len > buf.len() {
+                return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            }
+
+            let (results_metadata, consumed_len) =
+                Metadata::decode_all(&buf[idx..idx + metadata_len]);
+            if consumed_len != metadata_len {
+                return Err(PacketError::UnexpectedDataLength);
+            }
+            // Ignore any undecodable metadata types
+            let metadata = results_metadata.into_iter().filter_map(Result::ok).collect();
+            idx += consumed_len;
+
+            let mut bis = Vec::new();
+            while bis.len() < num_bis {
+                match BroadcastIsochronousStream::decode(&buf[idx..]) {
+                    (Ok(stream), consumed) => {
+                        bis.push(stream);
+                        idx += consumed;
+                    }
+                    (Err(e), _consumed) => {
+                        return Err(PacketError::InvalidParameter(e.to_string()));
+                    }
+                }
+            }
+
+            Ok((BroadcastIsochronousGroup { codec_id, codec_specific_configs, metadata, bis }, idx))
+        };
+        match decode_fn() {
+            Ok((obj, consumed)) => (Ok(obj), consumed),
+            Err(e) => (Err(e), buf.len()),
         }
-        let (results, consumed) = CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]);
-        if consumed != codec_config_len {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
-        }
-
-        let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect();
-        idx += codec_config_len;
-
-        let metadata_len = buf[idx] as usize;
-        idx += 1;
-        if idx + metadata_len > buf.len() {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
-        }
-
-        let (results_metadata, consumed_len) = Metadata::decode_all(&buf[idx..idx + metadata_len]);
-        if consumed_len != metadata_len {
-            return Err(PacketError::UnexpectedDataLength);
-        }
-        // Ignore any undecodable metadata types
-        let metadata = results_metadata.into_iter().filter_map(Result::ok).collect();
-        idx += consumed_len;
-
-        let mut bis = Vec::new();
-        while bis.len() < num_bis {
-            let (stream, len) = BroadcastIsochronousStream::decode(&buf[idx..])
-                .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
-            bis.push(stream);
-            idx += len;
-        }
-
-        Ok((BroadcastIsochronousGroup { codec_id, codec_specific_configs, metadata, bis }, idx))
     }
 }
 
@@ -247,9 +282,9 @@
 impl Decodable for BroadcastIsochronousStream {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < BroadcastIsochronousStream::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
         let mut idx = 0;
@@ -262,15 +297,18 @@
 
         let (results, consumed) = CodecConfiguration::decode_all(&buf[idx..idx + codec_config_len]);
         if consumed != codec_config_len {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len());
         }
         let codec_specific_configs = results.into_iter().filter_map(Result::ok).collect();
         idx += codec_config_len;
 
-        Ok((
-            BroadcastIsochronousStream { bis_index, codec_specific_config: codec_specific_configs },
+        (
+            Ok(BroadcastIsochronousStream {
+                bis_index,
+                codec_specific_config: codec_specific_configs,
+            }),
             idx,
-        ))
+        )
     }
 }
 
@@ -280,8 +318,8 @@
 
     use std::collections::HashSet;
 
-    use bt_common::generic_audio::codec_configuration::{FrameDuration, SamplingFrequency};
     use bt_common::generic_audio::AudioLocation;
+    use bt_common::generic_audio::codec_configuration::{FrameDuration, SamplingFrequency};
 
     #[test]
     fn broadcast_id() {
@@ -297,8 +335,8 @@
         let bytes = vec![0x0C, 0x0B, 0x0A];
         assert_eq!(buf, bytes);
 
-        let (got, bytes) = BroadcastId::decode(&bytes).expect("should succeed");
-        assert_eq!(got, id);
+        let (got, bytes) = BroadcastId::decode(&bytes);
+        assert_eq!(got, Ok(id));
         assert_eq!(bytes, BroadcastId::BYTE_SIZE);
         let got = BroadcastId::try_from(u32::from_le_bytes([0x0C, 0x0B, 0x0A, 0x00]))
             .expect("should succeed");
@@ -310,15 +348,15 @@
         let bytes = vec![0x0C, 0x0B, 0x0A];
         let broadcast_id = BroadcastId::try_from(0x000A0B0C).unwrap();
 
-        let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes).expect("should succeed");
-        assert_eq!(got, BroadcastAudioAnnouncement { broadcast_id });
+        let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes);
+        assert_eq!(got, Ok(BroadcastAudioAnnouncement { broadcast_id }));
         assert_eq!(consumed, 3);
 
         let bytes = vec![
             0x0C, 0x0B, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, /* some other additional data */
         ];
-        let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes).expect("should succeed");
-        assert_eq!(got, BroadcastAudioAnnouncement { broadcast_id });
+        let (got, consumed) = BroadcastAudioAnnouncement::decode(&bytes);
+        assert_eq!(got, Ok(BroadcastAudioAnnouncement { broadcast_id }));
         assert_eq!(consumed, 8);
     }
 
@@ -331,11 +369,10 @@
             0x05, 0x03, 0x03, 0x00, 0x00, 0x0C,  // audio location LTV
         ];
 
-        let (bis, _read_bytes) =
-            BroadcastIsochronousStream::decode(&buf[..]).expect("should not fail");
+        let (bis, _read_bytes) = BroadcastIsochronousStream::decode(&buf[..]);
         assert_eq!(
             bis,
-            BroadcastIsochronousStream {
+            Ok(BroadcastIsochronousStream {
                 bis_index: 0x01,
                 codec_specific_config: vec![
                     CodecConfiguration::SamplingFrequency(SamplingFrequency::F32000Hz),
@@ -346,7 +383,7 @@
                         AudioLocation::RightSurround
                     ])),
                 ],
-            }
+            })
         );
     }
 
@@ -361,8 +398,7 @@
             0x02, 0x03, 0x02, 0x02, 0x01,         // bis index, codec specific config len, frame duration LTV (bis #2)
         ];
 
-        let (big, _read_bytes) =
-            BroadcastIsochronousGroup::decode(&buf[..]).expect("should not fail");
+        let big = BroadcastIsochronousGroup::decode(&buf[..]).0.expect("should not fail");
         assert_eq!(
             big,
             BroadcastIsochronousGroup {
@@ -397,8 +433,7 @@
             0x01, 0x03, 0x02, 0x05, 0x08,         // bis index, codec specific config len, codec frame blocks LTV (big #2 / bis #2)
         ];
 
-        let (base, _read_bytes) =
-            BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail");
+        let base = BroadcastAudioSourceEndpoint::decode(&buf[..]).0.expect("should not fail");
         assert_eq!(base.presentation_delay_ms, 0x00302010);
         assert_eq!(base.big.len(), 2);
         assert_eq!(
@@ -449,8 +484,8 @@
             0x06,  // codec_specific_configuration_length of the 2nd BIS
             0x05, 0x03, 0x02, 0x00, 0x00, 0x00,  // front right audio channel
         ];
-        let (base, read_bytes) =
-            BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail");
+        let (base, read_bytes) = BroadcastAudioSourceEndpoint::decode(&buf[..]);
+        let base = base.expect("should not fail");
         assert_eq!(read_bytes, buf.len());
         assert_eq!(base.presentation_delay_ms, 20000);
         assert_eq!(base.big.len(), 1);
@@ -473,8 +508,8 @@
             0x03,  // codec_specific_configuration_length of the 1st BIS
             0x02, 0x01, 0x05,  // sampling frequency 24 kHz
         ];
-        let (base, read_bytes) =
-            BroadcastAudioSourceEndpoint::decode(&buf[..]).expect("should not fail");
+        let (base, read_bytes) = BroadcastAudioSourceEndpoint::decode(&buf[..]);
+        let base = base.expect("should not fail");
         assert_eq!(read_bytes, buf.len());
         assert_eq!(base.presentation_delay_ms, 20000);
         assert_eq!(base.big.len(), 1);
diff --git a/rust/bt-bass/src/client.rs b/rust/bt-bass/src/client.rs
index b48d8ed..f7b3f5b 100644
--- a/rust/bt-bass/src/client.rs
+++ b/rust/bt-bass/src/client.rs
@@ -8,8 +8,8 @@
 use std::collections::{HashMap, HashSet};
 use std::sync::Arc;
 
-use futures::stream::{BoxStream, FusedStream, SelectAll, Stream, StreamExt};
 use futures::Future;
+use futures::stream::{BoxStream, FusedStream, SelectAll, Stream, StreamExt};
 use log::warn;
 use parking_lot::Mutex;
 
@@ -177,8 +177,8 @@
             // record.
             let mut buf = vec![0; READ_CHARACTERISTIC_BUFFER_SIZE];
             match c.read(&mut buf[..]).await {
-                Ok(read_bytes) => match BroadcastReceiveState::decode(&buf[0..read_bytes]) {
-                    Ok((decoded, _decoded_bytes)) => {
+                Ok(read_bytes) => match BroadcastReceiveState::decode(&buf[0..read_bytes]).0 {
+                    Ok(decoded) => {
                         brs_map.insert(*c.handle(), decoded);
                         continue;
                     }
@@ -420,15 +420,15 @@
 
     use assert_matches::assert_matches;
     use futures::executor::block_on;
-    use futures::{pin_mut, FutureExt};
+    use futures::{FutureExt, pin_mut};
 
-    use bt_common::core::AdvertisingSetId;
     use bt_common::Uuid;
+    use bt_common::core::AdvertisingSetId;
+    use bt_gatt::Characteristic;
     use bt_gatt::test_utils::*;
     use bt_gatt::types::{
         AttributePermissions, CharacteristicProperties, CharacteristicProperty, Handle,
     };
-    use bt_gatt::Characteristic;
 
     const RECEIVE_STATE_1_HANDLE: Handle = Handle(1);
     const RECEIVE_STATE_2_HANDLE: Handle = Handle(2);
diff --git a/rust/bt-bass/src/client/event.rs b/rust/bt-bass/src/client/event.rs
index ca48fa8..0ff2768 100644
--- a/rust/bt-bass/src/client/event.rs
+++ b/rust/bt-bass/src/client/event.rs
@@ -15,9 +15,9 @@
 use bt_gatt::client::CharacteristicNotification;
 use bt_gatt::types::Error as BtGattError;
 
+use crate::client::KnownBroadcastSources;
 use crate::client::error::Error;
 use crate::client::error::ServiceError;
-use crate::client::KnownBroadcastSources;
 use crate::types::*;
 
 #[derive(Clone, Debug, PartialEq)]
@@ -150,7 +150,7 @@
                 }
                 Poll::Ready(Some(Ok(notification))) => {
                     let char_handle = notification.handle;
-                    let Ok((new_state, _)) =
+                    let (Ok(new_state), _) =
                         BroadcastReceiveState::decode(notification.value.as_slice())
                     else {
                         self.event_queue.push_back(Ok(Event::UnknownPacket));
@@ -190,7 +190,7 @@
                                 receive_state.big_encryption,
                             )));
                         } else {
-                            let other_events = Event::from_broadcast_receive_state(receive_state);
+                            let other_events = Event::from_broadcast_receive_state(&receive_state);
                             for e in other_events.into_iter() {
                                 multi_events.push_back(Ok(e));
                             }
diff --git a/rust/bt-bass/src/types.rs b/rust/bt-bass/src/types.rs
index 0ca916a..22baa25 100644
--- a/rust/bt-bass/src/types.rs
+++ b/rust/bt-bass/src/types.rs
@@ -7,7 +7,7 @@
 use bt_common::core::{AddressType, AdvertisingSetId, PaInterval};
 use bt_common::generic_audio::metadata_ltv::*;
 use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError};
-use bt_common::{decodable_enum, Uuid};
+use bt_common::{Uuid, decodable_enum};
 
 pub const ADDRESS_BYTE_SIZE: usize = 6;
 const NUM_SUBGROUPS_BYTE_SIZE: usize = 1;
@@ -78,12 +78,12 @@
 impl Decodable for RemoteScanStoppedOperation {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
-        if buf.len() < ControlPointOpcode::BYTE_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        const BYTE_SIZE: usize = ControlPointOpcode::BYTE_SIZE;
+        if buf.len() < BYTE_SIZE {
+            return (Err(PacketError::BufferTooSmall), buf.len());
         }
-        let _ = Self::check_opcode(buf[0])?;
-        Ok((RemoteScanStoppedOperation, ControlPointOpcode::BYTE_SIZE))
+        (Self::check_opcode(buf[0]).map(|_| RemoteScanStoppedOperation), BYTE_SIZE)
     }
 }
 
@@ -116,12 +116,12 @@
 impl Decodable for RemoteScanStartedOperation {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
-        if buf.len() < ControlPointOpcode::BYTE_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
+        const BYTE_SIZE: usize = ControlPointOpcode::BYTE_SIZE;
+        if buf.len() < BYTE_SIZE {
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
-        let _ = Self::check_opcode(buf[0])?;
-        Ok((RemoteScanStartedOperation, ControlPointOpcode::BYTE_SIZE))
+        (Self::check_opcode(buf[0]).map(|_| RemoteScanStartedOperation), BYTE_SIZE)
     }
 }
 
@@ -194,43 +194,50 @@
 impl Decodable for AddSourceOperation {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < Self::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
-        let _ = Self::check_opcode(buf[0])?;
-        let advertiser_address_type = AddressType::try_from(buf[1])?;
-        let mut advertiser_address = [0; ADDRESS_BYTE_SIZE];
-        advertiser_address.clone_from_slice(&buf[2..8]);
-        let advertising_sid = AdvertisingSetId(buf[8]);
-        let broadcast_id = BroadcastId::decode(&buf[9..12])?.0;
-        let pa_sync = PaSync::try_from(buf[12])?;
-        let pa_interval = PaInterval(u16::from_le_bytes(buf[13..15].try_into().unwrap()));
-        let num_subgroups = buf[15] as usize;
-        let mut subgroups = Vec::new();
+        let decode_fn = || {
+            let _ = Self::check_opcode(buf[0])?;
+            let advertiser_address_type = AddressType::try_from(buf[1])?;
+            let mut advertiser_address = [0; ADDRESS_BYTE_SIZE];
+            advertiser_address.clone_from_slice(&buf[2..8]);
+            let advertising_sid = AdvertisingSetId(buf[8]);
+            let broadcast_id = BroadcastId::decode(&buf[9..12]).0?;
+            let pa_sync = PaSync::try_from(buf[12])?;
+            let pa_interval = PaInterval(u16::from_le_bytes(buf[13..15].try_into().unwrap()));
+            let num_subgroups = buf[15] as usize;
+            let mut subgroups = Vec::new();
 
-        let mut idx: usize = 16;
-        for _i in 0..num_subgroups {
-            if buf.len() <= idx {
-                return Err(PacketError::UnexpectedDataLength);
+            let mut idx: usize = 16;
+            for _i in 0..num_subgroups {
+                if buf.len() <= idx {
+                    return Err(PacketError::UnexpectedDataLength);
+                }
+                let (decoded, consumed) = BigSubgroup::decode(&buf[idx..]);
+                subgroups.push(decoded?);
+                idx += consumed;
             }
-            let decoded = BigSubgroup::decode(&buf[idx..])?;
-            subgroups.push(decoded.0);
-            idx += decoded.1;
+            Ok((
+                Self {
+                    advertiser_address_type,
+                    advertiser_address,
+                    advertising_sid,
+                    broadcast_id,
+                    pa_sync,
+                    pa_interval,
+                    subgroups,
+                },
+                idx,
+            ))
+        };
+
+        match decode_fn() {
+            Ok((result, consumed)) => (Ok(result), consumed),
+            Err(e) => (Err(e), buf.len()),
         }
-        Ok((
-            Self {
-                advertiser_address_type,
-                advertiser_address,
-                advertising_sid,
-                broadcast_id,
-                pa_sync,
-                pa_interval,
-                subgroups,
-            },
-            idx,
-        ))
     }
 }
 
@@ -303,27 +310,34 @@
     type Error = PacketError;
 
     // Min size includes Source_ID, PA_Sync, PA_Interval, and Num_Subgroups params.
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < Self::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
-        let _ = Self::check_opcode(buf[0])?;
-        let source_id = buf[1];
-        let pa_sync = PaSync::try_from(buf[2])?;
-        let pa_interval = PaInterval(u16::from_le_bytes(buf[3..5].try_into().unwrap()));
-        let num_subgroups = buf[5] as usize;
-        let mut subgroups = Vec::new();
+        let decode_fn = || {
+            let _ = Self::check_opcode(buf[0])?;
+            let source_id = buf[1];
+            let pa_sync = PaSync::try_from(buf[2])?;
+            let pa_interval = PaInterval(u16::from_le_bytes(buf[3..5].try_into().unwrap()));
+            let num_subgroups = buf[5] as usize;
+            let mut subgroups = Vec::new();
 
-        let mut idx = 6;
-        for _i in 0..num_subgroups {
-            if buf.len() < idx + BigSubgroup::MIN_PACKET_SIZE {
-                return Err(PacketError::UnexpectedDataLength);
+            let mut idx = 6;
+            for _i in 0..num_subgroups {
+                if buf.len() < idx + BigSubgroup::MIN_PACKET_SIZE {
+                    return Err(PacketError::UnexpectedDataLength);
+                }
+                let decoded = BigSubgroup::decode(&buf[idx..]);
+                subgroups.push(decoded.0?);
+                idx += decoded.1;
             }
-            let decoded = BigSubgroup::decode(&buf[idx..])?;
-            subgroups.push(decoded.0);
-            idx += decoded.1;
+            Ok((Self { source_id, pa_sync, pa_interval, subgroups }, idx))
+        };
+
+        match decode_fn() {
+            Ok((obj, consumed)) => (Ok(obj), consumed),
+            Err(e) => (Err(e), buf.len()),
         }
-        Ok((Self { source_id, pa_sync, pa_interval, subgroups }, idx))
     }
 }
 
@@ -384,15 +398,22 @@
     type Error = PacketError;
 
     // Min size includes Source_ID, PA_Sync, PA_Interval, and Num_Subgroups params.
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < Self::PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
-        let _ = Self::check_opcode(buf[0])?;
-        let source_id = buf[1];
-        let mut broadcast_code = [0; Self::BROADCAST_CODE_LEN];
-        broadcast_code.copy_from_slice(&buf[2..2 + Self::BROADCAST_CODE_LEN]);
-        Ok((Self { source_id, broadcast_code }, Self::PACKET_SIZE))
+        let decode_fn = || {
+            let _ = Self::check_opcode(buf[0])?;
+            let source_id = buf[1];
+            let mut broadcast_code = [0; Self::BROADCAST_CODE_LEN];
+            broadcast_code.copy_from_slice(&buf[2..2 + Self::BROADCAST_CODE_LEN]);
+            Ok((Self { source_id, broadcast_code }, Self::PACKET_SIZE))
+        };
+
+        match decode_fn() {
+            Ok((obj, consumed)) => (Ok(obj), consumed),
+            Err(e) => (Err(e), buf.len()),
+        }
     }
 }
 
@@ -437,13 +458,19 @@
     type Error = PacketError;
 
     // Min size includes Source_ID, PA_Sync, PA_Interval, and Num_Subgroups params.
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < Self::PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
-        let _ = Self::check_opcode(buf[0])?;
-        let source_id = buf[1];
-        Ok((RemoveSourceOperation(source_id), Self::PACKET_SIZE))
+        let decode_fn = || {
+            let _ = Self::check_opcode(buf[0])?;
+            let source_id = buf[1];
+            Ok((RemoveSourceOperation(source_id), Self::PACKET_SIZE))
+        };
+        match decode_fn() {
+            Ok((obj, consumed)) => (Ok(obj), consumed),
+            Err(e) => (Err(e), buf.len()),
+        }
     }
 }
 
@@ -555,27 +582,33 @@
 impl Decodable for BigSubgroup {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < BigSubgroup::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
-        let bis_sync = u32::from_le_bytes(buf[0..4].try_into().unwrap());
-        let metadata_len = buf[4] as usize;
+        let decode_fn = || {
+            let bis_sync = u32::from_le_bytes(buf[0..4].try_into().unwrap());
+            let metadata_len = buf[4] as usize;
 
-        let mut start_idx = 5;
-        if buf.len() < start_idx + metadata_len {
-            return Err(PacketError::UnexpectedDataLength);
-        }
+            let mut start_idx = 5;
+            if buf.len() < start_idx + metadata_len {
+                return Err(PacketError::UnexpectedDataLength);
+            }
 
-        let (results_metadata, consumed_len) =
-            Metadata::decode_all(&buf[start_idx..start_idx + metadata_len]);
-        start_idx += consumed_len;
-        if start_idx != 5 + metadata_len {
-            return Err(PacketError::UnexpectedDataLength);
+            let (results_metadata, consumed_len) =
+                Metadata::decode_all(&buf[start_idx..start_idx + metadata_len]);
+            start_idx += consumed_len;
+            if start_idx != 5 + metadata_len {
+                return Err(PacketError::UnexpectedDataLength);
+            }
+            // Ignore any undecodable metadata types
+            let metadata = results_metadata.into_iter().filter_map(Result::ok).collect();
+            Ok((BigSubgroup { bis_sync: BisSync(bis_sync), metadata }, start_idx))
+        };
+        match decode_fn() {
+            Ok((obj, consumed)) => (Ok(obj), consumed),
+            Err(e) => (Err(e), buf.len()),
         }
-        // Ignore any undecodable metadata types
-        let metadata = results_metadata.into_iter().filter_map(Result::ok).collect();
-        Ok((BigSubgroup { bis_sync: BisSync(bis_sync), metadata }, start_idx))
     }
 }
 
@@ -647,12 +680,14 @@
 impl Decodable for BroadcastReceiveState {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() == 0 {
-            return Ok((Self::Empty, 0));
+            return (Ok(Self::Empty), 0);
         }
-        let res = ReceiveState::decode(&buf[..])?;
-        Ok((Self::NonEmpty(res.0), res.1))
+        match ReceiveState::decode(&buf[..]) {
+            (Ok(state), consumed) => (Ok(Self::NonEmpty(state)), consumed),
+            (Err(e), consumed) => (Err(e), consumed),
+        }
     }
 }
 
@@ -741,49 +776,63 @@
 impl Decodable for ReceiveState {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < Self::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
-        let source_id = buf[0];
-        let source_address_type = AddressType::try_from(buf[1])?;
-        let mut source_address = [0; ADDRESS_BYTE_SIZE];
-        source_address.clone_from_slice(&buf[2..8]);
-        let source_adv_sid = AdvertisingSetId(buf[8]);
-        let broadcast_id = BroadcastId::decode(&buf[9..12])?.0;
-        let pa_sync_state = PaSyncState::try_from(buf[12])?;
+        let decode_fn = || {
+            let source_id = buf[0];
+            let source_address_type = AddressType::try_from(buf[1])?;
+            let mut source_address = [0; ADDRESS_BYTE_SIZE];
+            source_address.clone_from_slice(&buf[2..8]);
+            let source_adv_sid = AdvertisingSetId(buf[8]);
+            let broadcast_id = BroadcastId::decode(&buf[9..12]).0?;
+            let pa_sync_state = PaSyncState::try_from(buf[12])?;
 
-        let decoded = EncryptionStatus::decode(&buf[13..])?;
-        let big_encryption = decoded.0;
-        let mut idx = 13 + decoded.1;
-        if buf.len() <= idx {
-            return Err(PacketError::UnexpectedDataLength);
-        }
-        let num_subgroups = buf[idx] as usize;
-        let mut subgroups = Vec::new();
-        idx += 1;
-        for _i in 0..num_subgroups {
+            let big_encryption;
+            let mut idx = 13;
+            match EncryptionStatus::decode(&buf[13..]) {
+                (Ok(encryption), consumed) => {
+                    big_encryption = encryption;
+                    idx += consumed;
+                }
+                (Err(e), _) => {
+                    return Err(e);
+                }
+            }
             if buf.len() <= idx {
                 return Err(PacketError::UnexpectedDataLength);
             }
-            let (subgroup, consumed) = BigSubgroup::decode(&buf[idx..])?;
-            subgroups.push(subgroup);
-            idx += consumed;
+            let num_subgroups = buf[idx] as usize;
+            let mut subgroups = Vec::new();
+            idx += 1;
+            for _i in 0..num_subgroups {
+                if buf.len() <= idx {
+                    return Err(PacketError::UnexpectedDataLength);
+                }
+                let (subgroup, consumed) = BigSubgroup::decode(&buf[idx..]);
+                subgroups.push(subgroup?);
+                idx += consumed;
+            }
+            Ok((
+                ReceiveState {
+                    source_id,
+                    source_address_type,
+                    source_address,
+                    source_adv_sid,
+                    broadcast_id,
+                    pa_sync_state,
+                    big_encryption,
+                    subgroups,
+                },
+                idx,
+            ))
+        };
+        match decode_fn() {
+            Ok((obj, consumed)) => (Ok(obj), consumed),
+            Err(e) => (Err(e), buf.len()),
         }
-        Ok((
-            ReceiveState {
-                source_id,
-                source_address_type,
-                source_address,
-                source_adv_sid,
-                broadcast_id,
-                pa_sync_state,
-                big_encryption,
-                subgroups,
-            },
-            idx,
-        ))
     }
 }
 
@@ -871,21 +920,21 @@
 impl Decodable for EncryptionStatus {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < 1 {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
         match buf[0] {
-            0x00 => Ok((Self::NotEncrypted, 1)),
-            0x01 => Ok((Self::BroadcastCodeRequired, 1)),
-            0x02 => Ok((Self::Decrypting, 1)),
+            0x00 => (Ok(Self::NotEncrypted), 1),
+            0x01 => (Ok(Self::BroadcastCodeRequired), 1),
+            0x02 => (Ok(Self::Decrypting), 1),
             0x03 => {
                 if buf.len() < 17 {
-                    return Err(PacketError::UnexpectedDataLength);
+                    return (Err(PacketError::UnexpectedDataLength), buf.len());
                 }
-                Ok((Self::BadCode(buf[1..17].try_into().unwrap()), 17))
+                (Ok(Self::BadCode(buf[1..17].try_into().unwrap())), 17)
             }
-            _ => Err(PacketError::OutOfRange),
+            _ => (Err(PacketError::OutOfRange), buf.len()),
         }
     }
 }
@@ -950,9 +999,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding not encrypted.
-        let decoded = EncryptionStatus::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, not_encrypted);
-        assert_eq!(decoded.1, 1);
+        let (decoded, len) = EncryptionStatus::decode(&bytes);
+        assert_eq!(decoded, Ok(not_encrypted));
+        assert_eq!(len, 1);
 
         // Encoding bad code status with code.
         let bad_code = EncryptionStatus::BadCode([
@@ -969,10 +1018,10 @@
         ];
         assert_eq!(buf, bytes);
 
-        // Deocoding bad code statsu with code.
-        let decoded = EncryptionStatus::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, bad_code);
-        assert_eq!(decoded.1, 17);
+        // Decoding bad code statsu with code.
+        let (decoded, len) = EncryptionStatus::decode(&bytes);
+        assert_eq!(decoded, Ok(bad_code));
+        assert_eq!(len, 17);
     }
 
     #[test]
@@ -992,11 +1041,11 @@
 
         // Cannot decode empty buffer.
         let buf = vec![];
-        let _ = EncryptionStatus::decode(&buf).expect_err("should fail");
+        let _ = EncryptionStatus::decode(&buf).0.expect_err("should fail");
 
         // Bad code status with no code.
         let buf = vec![0x03];
-        let _ = EncryptionStatus::decode(&buf).expect_err("should fail");
+        let _ = EncryptionStatus::decode(&buf).0.expect_err("should fail");
     }
 
     #[test]
@@ -1032,9 +1081,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding remote scan stopped.
-        let decoded = RemoteScanStoppedOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, stopped);
-        assert_eq!(decoded.1, 1);
+        let (decoded, len) = RemoteScanStoppedOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(stopped));
+        assert_eq!(len, 1);
     }
 
     #[test]
@@ -1049,9 +1098,9 @@
         assert_eq!(buf, vec![0x01]);
 
         // Decoding remote scan started.
-        let decoded = RemoteScanStartedOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, started);
-        assert_eq!(decoded.1, 1);
+        let (decoded, len) = RemoteScanStartedOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(started));
+        assert_eq!(len, 1);
     }
 
     #[test]
@@ -1077,9 +1126,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding operation with no subgroups.
-        let decoded = AddSourceOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, op);
-        assert_eq!(decoded.1, 16);
+        let (decoded, len) = AddSourceOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(op));
+        assert_eq!(len, 16);
     }
 
     #[test]
@@ -1111,9 +1160,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding operation with subgroups.
-        let decoded = AddSourceOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, op);
-        assert_eq!(decoded.1, 31);
+        let (decoded, len) = AddSourceOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(op));
+        assert_eq!(len, 31);
     }
 
     #[test]
@@ -1129,9 +1178,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding operation with no subgroups.
-        let decoded = ModifySourceOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, op);
-        assert_eq!(decoded.1, 6);
+        let (decoded, len) = ModifySourceOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(op));
+        assert_eq!(len, 6);
     }
 
     #[test]
@@ -1156,9 +1205,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding operation with subgroups.
-        let decoded = ModifySourceOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, op);
-        assert_eq!(decoded.1, 21);
+        let (decoded, len) = ModifySourceOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(op));
+        assert_eq!(len, 21);
     }
 
     #[test]
@@ -1182,9 +1231,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = SetBroadcastCodeOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, op);
-        assert_eq!(decoded.1, 18);
+        let (decoded, len) = SetBroadcastCodeOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(op));
+        assert_eq!(len, 18);
     }
 
     #[test]
@@ -1199,9 +1248,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = RemoveSourceOperation::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, op);
-        assert_eq!(decoded.1, 2);
+        let (decoded, len) = RemoveSourceOperation::decode(&bytes);
+        assert_eq!(decoded, Ok(op));
+        assert_eq!(len, 2);
     }
 
     #[test]
@@ -1233,9 +1282,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = BroadcastReceiveState::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, state);
-        assert_eq!(decoded.1, 31);
+        let (decoded, len) = BroadcastReceiveState::decode(&bytes);
+        assert_eq!(decoded, Ok(state));
+        assert_eq!(len, 31);
     }
 
     #[test]
@@ -1266,8 +1315,8 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = BroadcastReceiveState::decode(&bytes).expect("should succeed");
-        assert_eq!(decoded.0, state);
-        assert_eq!(decoded.1, 23);
+        let (decoded, len) = BroadcastReceiveState::decode(&bytes);
+        assert_eq!(decoded, Ok(state));
+        assert_eq!(len, 23);
     }
 }
diff --git a/rust/bt-battery/src/monitor/client.rs b/rust/bt-battery/src/monitor/client.rs
index 516cb72..d1ae91e 100644
--- a/rust/bt-battery/src/monitor/client.rs
+++ b/rust/bt-battery/src/monitor/client.rs
@@ -3,14 +3,14 @@
 // found in the LICENSE file.
 
 use bt_common::packet_encoding::Decodable;
+use bt_gatt::GattTypes;
 use bt_gatt::client::{CharacteristicNotification, PeerService, ServiceCharacteristic};
 use bt_gatt::types::{CharacteristicProperty, Error as GattLibraryError, Handle};
-use bt_gatt::GattTypes;
 use futures::stream::{BoxStream, FusedStream, SelectAll, Stream, StreamExt};
 use std::task::Poll;
 
 use crate::error::{Error, ServiceError};
-use crate::types::{BatteryLevel, BATTERY_LEVEL_UUID, READ_CHARACTERISTIC_BUFFER_SIZE};
+use crate::types::{BATTERY_LEVEL_UUID, BatteryLevel, READ_CHARACTERISTIC_BUFFER_SIZE};
 
 /// Represents the termination status of a Stream.
 #[derive(Clone, Copy, Debug, PartialEq, Default)]
@@ -70,9 +70,8 @@
         let result = futures::ready!(self.notification_streams.poll_next_unpin(cx));
         match result {
             Some(Ok(notification)) => {
-                let battery_level_result =
-                    BatteryLevel::decode(&notification.value[..]).map(|r| r.0).map_err(Into::into);
-                Poll::Ready(Some(battery_level_result))
+                let battery_level_result = BatteryLevel::decode(&notification.value[..]).0?;
+                Poll::Ready(Some(Ok(battery_level_result)))
             }
             Some(Err(e)) => {
                 // GATT Errors are not fatal and will be relayed to the stream.
@@ -151,8 +150,9 @@
         let (battery_level, _decoded_bytes) = {
             let mut buf = vec![0; READ_CHARACTERISTIC_BUFFER_SIZE];
             let read_bytes = primary_battery_level_characteristic.read(&mut buf[..]).await?;
-            BatteryLevel::decode(&buf[0..read_bytes])?
+            BatteryLevel::decode(&buf[0..read_bytes])
         };
+        let battery_level = battery_level?;
 
         // Subscribe to notifications on the battery level characteristic if it
         // is supported.
@@ -199,13 +199,13 @@
 pub(crate) mod tests {
     use super::*;
 
-    use bt_common::packet_encoding::Error as PacketError;
     use bt_common::Uuid;
+    use bt_common::packet_encoding::Error as PacketError;
     use bt_gatt::test_utils::{FakeClient, FakePeerService, FakeTypes};
     use bt_gatt::types::{
         AttributePermissions, Characteristic, CharacteristicProperties, GattError,
     };
-    use futures::{pin_mut, FutureExt};
+    use futures::{FutureExt, pin_mut};
 
     pub(crate) const BATTERY_LEVEL_HANDLE: Handle = Handle(0x1);
     pub(crate) fn fake_battery_service(battery_level: u8) -> FakePeerService {
diff --git a/rust/bt-battery/src/types.rs b/rust/bt-battery/src/types.rs
index b7980c5..ccc4428 100644
--- a/rust/bt-battery/src/types.rs
+++ b/rust/bt-battery/src/types.rs
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use bt_common::packet_encoding::{Decodable, Error as PacketError};
 use bt_common::Uuid;
+use bt_common::packet_encoding::{Decodable, Error as PacketError};
 
 /// The UUID of the GATT battery service.
 /// Defined in Assigned Numbers Section 3.4.2.
@@ -21,13 +21,13 @@
 impl Decodable for BatteryLevel {
     type Error = PacketError;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < 1 {
-            return Err(PacketError::UnexpectedDataLength);
+            return (Err(PacketError::UnexpectedDataLength), buf.len());
         }
 
         let level_percent = buf[0].clamp(0, 100);
-        Ok((BatteryLevel(level_percent), 1))
+        (Ok(BatteryLevel(level_percent)), 1)
     }
 }
 
@@ -38,31 +38,32 @@
     #[test]
     fn decode_battery_level_success() {
         let buf = [55];
-        let (parsed, parsed_size) = BatteryLevel::decode(&buf).expect("valid battery level");
-        assert_eq!(parsed, BatteryLevel(55));
+        let (parsed, parsed_size) = BatteryLevel::decode(&buf);
+        assert_eq!(parsed, Ok(BatteryLevel(55)));
         assert_eq!(parsed_size, 1);
     }
 
     #[test]
     fn decode_large_battery_level_clamped() {
         let buf = [125]; // Too large, expected to be a percentage value.
-        let (parsed, parsed_size) = BatteryLevel::decode(&buf).expect("valid battery level");
-        assert_eq!(parsed, BatteryLevel(100));
+        let (parsed, parsed_size) = BatteryLevel::decode(&buf);
+        assert_eq!(parsed, Ok(BatteryLevel(100)));
         assert_eq!(parsed_size, 1);
     }
 
     #[test]
     fn decode_large_buf_success() {
         let large_buf = [19, 0]; // Only expect a single u8 for the level.
-        let (parsed, parsed_size) = BatteryLevel::decode(&large_buf).expect("valid battery level");
-        assert_eq!(parsed, BatteryLevel(19)); // Only the first byte should be read.
+        let (parsed, parsed_size) = BatteryLevel::decode(&large_buf);
+        assert_eq!(parsed, Ok(BatteryLevel(19))); // Only the first byte should be read.
         assert_eq!(parsed_size, 1);
     }
 
     #[test]
     fn decode_invalid_battery_level_buf_is_error() {
         let buf = [];
-        let result = BatteryLevel::decode(&buf);
+        let (result, parsed_size) = BatteryLevel::decode(&buf);
         assert_eq!(result, Err(PacketError::UnexpectedDataLength));
+        assert_eq!(parsed_size, 0);
     }
 }
diff --git a/rust/bt-broadcast-assistant/src/assistant/event.rs b/rust/bt-broadcast-assistant/src/assistant/event.rs
index e1e444c..7290e47 100644
--- a/rust/bt-broadcast-assistant/src/assistant/event.rs
+++ b/rust/bt-broadcast-assistant/src/assistant/event.rs
@@ -8,14 +8,14 @@
 use std::task::Poll;
 
 use bt_bap::types::{BroadcastAudioSourceEndpoint, BroadcastId};
+use bt_common::PeerId;
 use bt_common::packet_encoding::Decodable;
 use bt_common::packet_encoding::Error as PacketError;
-use bt_common::PeerId;
 use bt_gatt::central::{AdvertisingDatum, ScanResult};
 
 use crate::assistant::{
-    DiscoveredBroadcastSources, Error, BASIC_AUDIO_ANNOUNCEMENT_SERVICE,
-    BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE,
+    BASIC_AUDIO_ANNOUNCEMENT_SERVICE, BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE,
+    DiscoveredBroadcastSources, Error,
 };
 use crate::types::BroadcastSource;
 
@@ -59,11 +59,11 @@
                 continue;
             };
             if *uuid == BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE {
-                let (bid, _) = BroadcastId::decode(data.as_slice())?;
+                let bid = BroadcastId::decode(data.as_slice()).0?;
                 source.get_or_insert(BroadcastSource::default()).with_broadcast_id(bid);
             } else if *uuid == BASIC_AUDIO_ANNOUNCEMENT_SERVICE {
                 // TODO(dayeonglee): revisit when we implement periodic advertisement.
-                let (base, _) = BroadcastAudioSourceEndpoint::decode(data.as_slice())?;
+                let base = BroadcastAudioSourceEndpoint::decode(data.as_slice()).0?;
                 source.get_or_insert(BroadcastSource::default()).with_endpoint(base);
             }
         }
diff --git a/rust/bt-common/src/core.rs b/rust/bt-common/src/core.rs
index f7d94e5..db4a1d9 100644
--- a/rust/bt-common/src/core.rs
+++ b/rust/bt-common/src/core.rs
@@ -162,18 +162,18 @@
 impl crate::packet_encoding::Decodable for CodecId {
     type Error = crate::packet_encoding::Error;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < 5 {
-            return Err(crate::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(crate::packet_encoding::Error::UnexpectedDataLength), buf.len());
         }
         let format = buf[0].into();
         if format != CodingFormat::VendorSpecific {
             // Maybe don't ignore the company and vendor id, and check if they are wrong.
-            return Ok((Self::Assigned(format), 5));
+            return (Ok(Self::Assigned(format)), 5);
         }
         let company_id = u16::from_le_bytes([buf[1], buf[2]]).into();
         let vendor_specific_codec_id = u16::from_le_bytes([buf[3], buf[4]]);
-        Ok((Self::VendorSpecific { company_id, vendor_specific_codec_id }, 5))
+        (Ok(Self::VendorSpecific { company_id, vendor_specific_codec_id }), 5)
     }
 }
 
@@ -239,34 +239,34 @@
     #[test]
     fn decode_codec_id() {
         let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
-        let (codec_id, _) = CodecId::decode(&assigned[..]).expect("should succeed");
-        assert_eq!(codec_id, CodecId::Assigned(CodingFormat::ALawLog));
+        let (codec_id, _) = CodecId::decode(&assigned[..]);
+        assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog)));
 
         let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
-        let (codec_id, _) = CodecId::decode(&vendor_specific[..]).expect("should succeed");
+        let (codec_id, _) = CodecId::decode(&vendor_specific[..]);
         assert_eq!(
             codec_id,
-            CodecId::VendorSpecific {
+            Ok(CodecId::VendorSpecific {
                 company_id: (0xFD36 as u16).into(),
                 vendor_specific_codec_id: 0x2211
-            }
+            })
         );
     }
 
     #[test]
     fn encode_codec_id() {
         let assigned = [0x01, 0x00, 0x00, 0x00, 0x00];
-        let (codec_id, _) = CodecId::decode(&assigned[..]).expect("should succeed");
-        assert_eq!(codec_id, CodecId::Assigned(CodingFormat::ALawLog));
+        let (codec_id, _) = CodecId::decode(&assigned[..]);
+        assert_eq!(codec_id, Ok(CodecId::Assigned(CodingFormat::ALawLog)));
 
         let vendor_specific = [0xFF, 0x36, 0xFD, 0x11, 0x22];
-        let (codec_id, _) = CodecId::decode(&vendor_specific[..]).expect("should succeed");
+        let (codec_id, _) = CodecId::decode(&vendor_specific[..]);
         assert_eq!(
             codec_id,
-            CodecId::VendorSpecific {
+            Ok(CodecId::VendorSpecific {
                 company_id: (0xFD36 as u16).into(),
                 vendor_specific_codec_id: 0x2211
-            }
+            })
         );
     }
 }
diff --git a/rust/bt-common/src/core/ltv.rs b/rust/bt-common/src/core/ltv.rs
index 6a1083a..e8c7498 100644
--- a/rust/bt-common/src/core/ltv.rs
+++ b/rust/bt-common/src/core/ltv.rs
@@ -54,21 +54,15 @@
                 return (results, std::cmp::min(buf.len(), total_consumed));
             }
             let indicated_len = buf[total_consumed] as usize;
-            match Self::decode(&buf[total_consumed..=total_consumed + indicated_len]) {
-                Ok((item, consumed)) => {
+            let range_end = std::cmp::min(buf.len() - 1, total_consumed + indicated_len);
+            match Self::decode(&buf[total_consumed..=range_end]) {
+                (Ok(item), consumed) => {
                     results.push(Ok(item));
                     total_consumed += consumed;
                 }
-                // If we are missing the type / length completely, or missing some of the data, we
-                // can't continue
-                Err(e @ Error::MissingType) | Err(e @ Error::MissingData(_)) => {
+                (Err(e), consumed) => {
                     results.push(Err(e));
-                    return (results, total_consumed);
-                }
-                Err(e) => {
-                    results.push(Err(e));
-                    // Consume the bytes
-                    total_consumed += indicated_len + 1;
+                    total_consumed += consumed;
                 }
             }
         }
@@ -134,28 +128,36 @@
     }
 }
 
-impl<T: LtValue> Decodable for T {
+impl<T> Decodable for T
+where
+    T: LtValue,
+{
     type Error = Error<T::Type>;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < 2 {
-            return Err(Error::MissingType);
+            return (Err(Error::MissingType), buf.len());
         }
         let indicated_len = buf[0] as usize;
+        let too_short = buf.len() < indicated_len + 1;
+
         let Some(ty) = Self::type_from_octet(buf[1]) else {
-            return Err(Error::UnrecognizedType(Self::NAME.to_owned(), buf[1]));
+            return (
+                Err(Error::UnrecognizedType(Self::NAME.to_owned(), buf[1])),
+                if too_short { buf.len() } else { indicated_len + 1 },
+            );
         };
-        if buf.len() < indicated_len + 1 {
-            return Err(Error::MissingData(ty));
+        if too_short {
+            return (Err(Error::MissingData(ty)), buf.len());
         }
         let size_range = Self::length_range_from_type(ty);
         let remaining_len = (buf.len() - 1) as u8;
         if !size_range.contains(&remaining_len) {
-            return Err(Error::LengthOutOfRange(remaining_len, ty, size_range));
+            return (Err(Error::LengthOutOfRange(remaining_len, ty, size_range)), buf.len());
         }
         match Self::decode_value(&ty, &buf[2..=indicated_len]) {
-            Err(e) => Err(Error::TypeFailedToDecode(ty, e)),
-            Ok(s) => Ok((s, indicated_len + 1)),
+            Err(e) => (Err(Error::TypeFailedToDecode(ty, e)), indicated_len + 1),
+            Ok(s) => (Ok(s), indicated_len + 1),
         }
     }
 }
@@ -304,6 +306,15 @@
     }
 
     #[test]
+    fn decode_wronglength() {
+        let encoded = [0x03, 0x02, 0x10, 0x01, 0x04, 0x03, 0x10, 0x01];
+        let (decoded, consumed) = TestValues::decode_all(&encoded);
+        assert_eq!(consumed, encoded.len());
+        assert_eq!(decoded[0], Ok(TestValues::TwoBytes(4097)));
+        assert_eq!(decoded[1], Err(Error::MissingData(TestType::TwoBytesLittleEndian)));
+    }
+
+    #[test]
     fn encode_twobytes() {
         let value = TestValues::TwoBytes(0x0A0B);
         let mut buf = [0; 4];
diff --git a/rust/bt-common/src/generic_audio/metadata_ltv.rs b/rust/bt-common/src/generic_audio/metadata_ltv.rs
index 5e95215..75b4178 100644
--- a/rust/bt-common/src/generic_audio/metadata_ltv.rs
+++ b/rust/bt-common/src/generic_audio/metadata_ltv.rs
@@ -4,7 +4,7 @@
 
 use crate::core::ltv::LtValue;
 use crate::packet_encoding::Error as PacketError;
-use crate::{decodable_enum, CompanyId};
+use crate::{CompanyId, decodable_enum};
 
 use crate::generic_audio::ContextType;
 
@@ -286,9 +286,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 4);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 4);
     }
 
     #[test]
@@ -304,9 +304,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 4);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 4);
     }
 
     #[test]
@@ -321,9 +321,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 3);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 3);
     }
 
     #[test]
@@ -338,9 +338,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 5);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 5);
     }
 
     #[test]
@@ -355,9 +355,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 4);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 4);
     }
 
     #[test]
@@ -372,9 +372,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 3);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 3);
     }
 
     #[test]
@@ -389,9 +389,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 6);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 6);
     }
 
     #[test]
@@ -406,9 +406,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 3);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 3);
     }
 
     #[test]
@@ -423,9 +423,9 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadata::decode(&buf).expect("should succeed");
-        assert_eq!(decoded.0, test);
-        assert_eq!(decoded.1, 2);
+        let (decoded, len) = Metadata::decode(&buf);
+        assert_eq!(decoded, Ok(test));
+        assert_eq!(len, 2);
     }
 
     #[test]
@@ -437,15 +437,15 @@
 
         // Not enough length for Length and Type for decoding.
         let buf = vec![0x03];
-        let _ = Metadata::decode(&buf).expect_err("should fail");
+        let _ = Metadata::decode(&buf).0.expect_err("should fail");
 
         // Not enough length for Value field for decoding.
         let buf = vec![0x02, 0x01, 0x02];
-        let _ = Metadata::decode(&buf).expect_err("should fail");
+        let _ = Metadata::decode(&buf).0.expect_err("should fail");
 
         // Buffer length does not match Length value for decoding.
         let buf = vec![0x03, 0x03, 0x61];
-        let _ = Metadata::decode(&buf).expect_err("should fail");
+        let _ = Metadata::decode(&buf).0.expect_err("should fail");
     }
 
     #[test]
diff --git a/rust/bt-common/src/packet_encoding.rs b/rust/bt-common/src/packet_encoding.rs
index 9ff05d4..49f3e63 100644
--- a/rust/bt-common/src/packet_encoding.rs
+++ b/rust/bt-common/src/packet_encoding.rs
@@ -9,34 +9,43 @@
 pub trait Decodable: ::core::marker::Sized {
     type Error;
 
-    /// Decodes into a new object with the number of bytes that were decoded, or
-    /// returns an error.
-    fn decode(buf: &[u8]) -> ::core::result::Result<(Self, usize), Self::Error>;
+    /// Decodes into a new object or an error, and the number of bytes that
+    /// the decoding consumed.  Should attempt to consume the entire item from
+    /// the buffer in the case of an error.  If the item end cannot be determined,
+    /// return an error and consume the entirety of the bufer (`buf.len()`)
+    fn decode(buf: &[u8]) -> (::core::result::Result<Self, Self::Error>, usize);
 
-    /// Tries to decode a collection of a decodable item from a buffer.
-    /// If any item fails to decode, fails with an Error and the previously
-    /// decoded items.
-    /// Will only decode up to max items. If None, will decode the entire
-    /// buffer.
+    /// Tries to decode a collection of this object concatenated in a buffer.
+    /// Returns a vector of items (or errors) and the number of bytes consumed to
+    /// decode them.
+    /// Continues to decode items until the buffer is consumed or the max items.
+    /// If None, will decode the entire buffer.
     fn decode_multiple(
         buf: &[u8],
         max: Option<usize>,
-    ) -> ::core::result::Result<(Vec<Self>, usize), (Vec<Self>, usize, Self::Error)> {
+    ) -> (Vec<::core::result::Result<Self, Self::Error>>, usize) {
         let mut idx = 0;
         let mut result = Vec::new();
         while idx < buf.len() && Some(result.len()) != max {
-            match Self::decode(&buf[idx..]) {
-                Err(e) => return Err((result, idx, e)),
-                Ok((item, consumed)) => {
-                    result.push(item);
-                    idx += consumed;
-                }
-            }
+            let (one_result, consumed) = Self::decode(&buf[idx..]);
+            result.push(one_result);
+            idx += consumed;
         }
-        Ok((result, idx))
+        (result, idx)
     }
 }
 
+/// A decodable type that has an exact size in bytes.
+pub trait FixedSizeDecodable: ::core::marker::Sized {
+    type Error;
+    const WRONG_SIZE_ERROR: Self::Error;
+    const BYTE_SIZE: usize;
+
+    /// Decodes the value.  This function assumes that buf is of at least
+    /// BYTE_SIZE, and assumes that BYTE_SIZE bytes are consumed to decode.
+    fn decode_checked(buf: &[u8]) -> core::result::Result<Self, Self::Error>;
+}
+
 /// An encodable type can write itself into a byte buffer.
 pub trait Encodable {
     type Error;
diff --git a/rust/bt-gatt/src/server.rs b/rust/bt-gatt/src/server.rs
index 8a6f3b3..28efd8e 100644
--- a/rust/bt-gatt/src/server.rs
+++ b/rust/bt-gatt/src/server.rs
@@ -13,7 +13,7 @@
 pub struct ServiceId(u64);
 
 impl ServiceId {
-    pub fn new(id: u64) -> Self {
+    pub const fn new(id: u64) -> Self {
         Self(id)
     }
 }
diff --git a/rust/bt-gatt/src/test_utils.rs b/rust/bt-gatt/src/test_utils.rs
index 873689f..46aa379 100644
--- a/rust/bt-gatt/src/test_utils.rs
+++ b/rust/bt-gatt/src/test_utils.rs
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 use bt_common::core::{Address, AddressType};
-use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
-use futures::future::{ready, Ready};
 use futures::Stream;
+use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
+use futures::future::{Ready, ready};
 use parking_lot::Mutex;
 use std::collections::HashMap;
 use std::sync::Arc;
@@ -18,7 +18,7 @@
 use crate::periodic_advertising::{PeriodicAdvertising, SyncReport};
 use crate::pii::GetPeerAddr;
 use crate::server::{self, LocalService, ReadResponder, ServiceDefinition, WriteResponder};
-use crate::{types::*, GattTypes, ServerTypes};
+use crate::{GattTypes, ServerTypes, types::*};
 
 #[derive(Default)]
 struct FakePeerServiceInner {
@@ -362,6 +362,7 @@
     }
 }
 
+#[derive(Debug)]
 pub enum FakeServerEvent {
     ReadResponded {
         service_id: server::ServiceId,
diff --git a/rust/bt-gatt/src/types.rs b/rust/bt-gatt/src/types.rs
index aefbb78..c9b72d3 100644
--- a/rust/bt-gatt/src/types.rs
+++ b/rust/bt-gatt/src/types.rs
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::collections::HashSet;
-
 use bt_common::{PeerId, Uuid};
 use thiserror::Error;
 
@@ -264,9 +262,12 @@
 
 impl CharacteristicProperties {
     pub fn is_disjoint(&self, other: &Self) -> bool {
-        let this_properties: HashSet<_> = HashSet::from_iter(self.0.iter());
-        let other_properties: HashSet<_> = HashSet::from_iter(other.0.iter());
-        this_properties.is_disjoint(&other_properties)
+        for property in self.0.iter() {
+            if other.0.contains(&property) {
+                return false;
+            }
+        }
+        true
     }
 
     pub fn contains(&self, property: CharacteristicProperty) -> bool {
diff --git a/rust/bt-pacs/src/lib.rs b/rust/bt-pacs/src/lib.rs
index 3e34f35..412e574 100644
--- a/rust/bt-pacs/src/lib.rs
+++ b/rust/bt-pacs/src/lib.rs
@@ -37,19 +37,24 @@
 impl Decodable for PacRecord {
     type Error = bt_common::packet_encoding::Error;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         let mut idx = 0;
-        let (codec_id, consumed) = CodecId::decode(&buf[idx..])?;
-        idx += consumed;
+        let codec_id = match CodecId::decode(&buf[idx..]) {
+            (Ok(codec_id), consumed) => {
+                idx += consumed;
+                codec_id
+            }
+            (Err(e), _) => return (Err(e), buf.len()),
+        };
         let codec_specific_capabilites_length = buf[idx] as usize;
         idx += 1;
         if idx + codec_specific_capabilites_length > buf.len() {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len());
         }
         let (results, consumed) =
             CodecCapability::decode_all(&buf[idx..idx + codec_specific_capabilites_length]);
         if consumed != codec_specific_capabilites_length {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len());
         }
         let codec_specific_capabilities = results.into_iter().filter_map(Result::ok).collect();
         idx += consumed;
@@ -57,16 +62,16 @@
         let metadata_length = buf[idx] as usize;
         idx += 1;
         if idx + metadata_length > buf.len() {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len());
         }
         let (results, consumed) = Metadata::decode_all(&buf[idx..idx + metadata_length]);
         if consumed != metadata_length {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len());
         }
         let metadata = results.into_iter().filter_map(Result::ok).collect();
         idx += consumed;
 
-        Ok((Self { codec_id, codec_specific_capabilities, metadata }, idx))
+        (Ok(Self { codec_id, codec_specific_capabilities, metadata }), idx)
     }
 }
 
@@ -124,8 +129,8 @@
     let mut next_idx = 1;
     let mut capabilities = Vec::with_capacity(num_of_pac_records);
     for _ in 0..num_of_pac_records {
-        let (cap, consumed) = PacRecord::decode(&value[next_idx..])?;
-        capabilities.push(cap);
+        let (cap, consumed) = PacRecord::decode(&value[next_idx..]);
+        capabilities.push(cap?);
         next_idx += consumed;
     }
     Ok(capabilities)
@@ -197,14 +202,14 @@
 impl Decodable for AudioLocations {
     type Error = bt_common::packet_encoding::Error;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() != 4 {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), buf.len());
         }
         let locations =
             AudioLocation::from_bits(u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]))
                 .collect();
-        Ok((AudioLocations { locations }, 4))
+        (Ok(AudioLocations { locations }), 4)
     }
 }
 
@@ -249,12 +254,12 @@
         value: &[u8],
     ) -> Result<Self, bt_common::packet_encoding::Error> {
         let handle = characteristic.handle;
-        let (locations, _) = AudioLocations::decode(value)?;
+        let locations = AudioLocations::decode(value).0?;
         Ok(Self { handle, locations })
     }
 
     fn update(&mut self, new_value: &[u8]) -> Result<&mut Self, bt_common::packet_encoding::Error> {
-        self.locations = AudioLocations::decode(new_value)?.0;
+        self.locations = AudioLocations::decode(new_value).0?;
         Ok(self)
     }
 }
@@ -282,12 +287,12 @@
         value: &[u8],
     ) -> Result<Self, bt_common::packet_encoding::Error> {
         let handle = characteristic.handle;
-        let (locations, _) = AudioLocations::decode(value)?;
+        let locations = AudioLocations::decode(value).0?;
         Ok(Self { handle, locations })
     }
 
     fn update(&mut self, new_value: &[u8]) -> Result<&mut Self, bt_common::packet_encoding::Error> {
-        self.locations = AudioLocations::decode(new_value)?.0;
+        self.locations = AudioLocations::decode(new_value).0?;
         Ok(self)
     }
 }
@@ -301,15 +306,15 @@
 impl Decodable for AvailableContexts {
     type Error = bt_common::packet_encoding::Error;
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+    fn decode(buf: &[u8]) -> (core::result::Result<Self, Self::Error>, usize) {
         if buf.len() < 2 {
-            return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
+            return (Err(bt_common::packet_encoding::Error::UnexpectedDataLength), 2);
         }
         let encoded = u16::from_le_bytes([buf[0], buf[1]]);
         if encoded == 0 {
-            Ok((Self::NotAvailable, 2))
+            (Ok(Self::NotAvailable), 2)
         } else {
-            Ok((Self::Available(ContextType::from_bits(encoded).collect()), 2))
+            (Ok(Self::Available(ContextType::from_bits(encoded).collect())), 2)
         }
     }
 }
@@ -373,8 +378,8 @@
         if value.len() < 4 {
             return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
         }
-        let sink = AvailableContexts::decode(&value[0..2])?.0;
-        let source = AvailableContexts::decode(&value[2..4])?.0;
+        let sink = AvailableContexts::decode(&value[0..2]).0?;
+        let source = AvailableContexts::decode(&value[2..4]).0?;
         Ok(Self { handle, sink, source })
     }
 
@@ -385,8 +390,8 @@
         if new_value.len() != 4 {
             return Err(bt_common::packet_encoding::Error::UnexpectedDataLength);
         }
-        let sink = AvailableContexts::decode(&new_value[0..2])?.0;
-        let source = AvailableContexts::decode(&new_value[2..4])?.0;
+        let sink = AvailableContexts::decode(&new_value[0..2]).0?;
+        let source = AvailableContexts::decode(&new_value[2..4]).0?;
         self.sink = sink;
         self.source = source;
         Ok(self)