[bt-common] LtValue trait, CodecCapabilities

Add LtValue trait and helper functions to help with defining types
that are length-type-value structure.

Add CompanyId, CodingFormat from core spec.

Migrate core::fmt::Display to std::fmt::Display for PeerId to not
conflict with core mod.

Add packet_encoding mod.

Bug: b/308483293
Tests: cargo test
Change-Id: Ie4d4e26f83f632ee93853b62a2c1f41dc2b5c8a8
Reviewed-on: https://bluetooth-review.git.corp.google.com/c/bluetooth/+/1320
Reviewed-by: Dayeong Lee <dayeonglee@google.com>
diff --git a/rust/bt-common/Cargo.toml b/rust/bt-common/Cargo.toml
index 0b1c788..497acbb 100644
--- a/rust/bt-common/Cargo.toml
+++ b/rust/bt-common/Cargo.toml
@@ -4,10 +4,10 @@
 edition = "2021"
 license = "BSD-2-Clause"
 
-[dependencies.uuid]
-version = "1.4.1"
-features = ["macro-diagnostics"]
-
 [dependencies]
 bitflags = "2.4.0"
 thiserror = "1.0"
+
+[dependencies.uuid]
+version = "1.4.1"
+features = ["macro-diagnostics"]
diff --git a/rust/bt-common/src/company_id.rs b/rust/bt-common/src/company_id.rs
new file mode 100644
index 0000000..78addec
--- /dev/null
+++ b/rust/bt-common/src/company_id.rs
@@ -0,0 +1,28 @@
+// Copyright 2023 Google LLC
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// A Bluetooth Company ID, which is assigned by the Bluetotoh SIG.
+/// Company identifiers are unique numbers assigned by the Bluetooth SIG to
+/// member companies requesting one. Referenced in the Bluetooth Core Spec in
+/// many commands and formats. See the Assigned Number Document for a reference.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CompanyId(u16);
+
+impl From<u16> for CompanyId {
+    fn from(value: u16) -> Self {
+        Self(value)
+    }
+}
+
+impl From<CompanyId> for u16 {
+    fn from(value: CompanyId) -> Self {
+        value.0
+    }
+}
+
+impl core::fmt::Display for CompanyId {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Company (ID {:02x})", self.0)
+    }
+}
diff --git a/rust/bt-common/src/core.rs b/rust/bt-common/src/core.rs
index 16bb6ec..f7d8c7b 100644
--- a/rust/bt-common/src/core.rs
+++ b/rust/bt-common/src/core.rs
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/// Traits and utilities to handle length-type-value structures.
+pub mod ltv;
+
 use crate::packet_encoding::{Encodable, Error as PacketError};
 
 /// Represents the Advertising Set ID which is 1 byte long.
@@ -45,6 +48,74 @@
     }
 }
 
+/// Coding Format as defined by the Assigned Numbers Document. Section 2.11.
+/// Referenced in the Core Spec 5.3, Volume 4, Part E, Section 7 as well as
+/// various other profile specifications.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum CodingFormat {
+    MuLawLog,
+    ALawLog,
+    Cvsd,
+    Transparent,
+    LinearPcm,
+    Msbc,
+    Lc3,
+    G729a,
+    VendorSpecific,
+    Unrecognized(u8),
+}
+
+impl From<u8> for CodingFormat {
+    fn from(value: u8) -> Self {
+        match value {
+            0x00 => Self::MuLawLog,
+            0x01 => Self::ALawLog,
+            0x02 => Self::Cvsd,
+            0x03 => Self::Transparent,
+            0x04 => Self::LinearPcm,
+            0x05 => Self::Msbc,
+            0x06 => Self::Lc3,
+            0x07 => Self::G729a,
+            0xFF => Self::VendorSpecific,
+            x => Self::Unrecognized(x),
+        }
+    }
+}
+
+impl From<CodingFormat> for u8 {
+    fn from(value: CodingFormat) -> Self {
+        match value {
+            CodingFormat::MuLawLog => 0x00,
+            CodingFormat::ALawLog => 0x01,
+            CodingFormat::Cvsd => 0x02,
+            CodingFormat::Transparent => 0x03,
+            CodingFormat::LinearPcm => 0x04,
+            CodingFormat::Msbc => 0x05,
+            CodingFormat::Lc3 => 0x06,
+            CodingFormat::G729a => 0x07,
+            CodingFormat::VendorSpecific => 0xFF,
+            CodingFormat::Unrecognized(x) => x,
+        }
+    }
+}
+
+impl core::fmt::Display for CodingFormat {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            CodingFormat::MuLawLog => write!(f, "µ-law log"),
+            CodingFormat::ALawLog => write!(f, "A-law log"),
+            CodingFormat::Cvsd => write!(f, "CVSD"),
+            CodingFormat::Transparent => write!(f, "Transparent"),
+            CodingFormat::LinearPcm => write!(f, "Linear PCM"),
+            CodingFormat::Msbc => write!(f, "mSBC"),
+            CodingFormat::Lc3 => write!(f, "LC3"),
+            CodingFormat::G729a => write!(f, "G.729A"),
+            CodingFormat::VendorSpecific => write!(f, "Vendor Specific"),
+            CodingFormat::Unrecognized(x) => write!(f, "Unrecognized ({x})"),
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -60,7 +131,7 @@
 
     #[test]
     fn encode_pa_interval_fails() {
-        let mut buf = [0; 1];  // Not enough buffer space.
+        let mut buf = [0; 1]; // Not enough buffer space.
         let interval = PaInterval(0x1004);
 
         interval.encode(&mut buf[..]).expect_err("should fail");
diff --git a/rust/bt-common/src/core/ltv.rs b/rust/bt-common/src/core/ltv.rs
new file mode 100644
index 0000000..5baab7d
--- /dev/null
+++ b/rust/bt-common/src/core/ltv.rs
@@ -0,0 +1,313 @@
+// Copyright 2023 Google LLC
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::packet_encoding::{Decodable, Encodable};
+
+/// Implement Ltv when a collection of types is represented in the Bluetooth
+/// specifications as a length-type-value structure.  They should have an
+/// associated type which can be retrieved from a type byte.
+pub trait LtValue: Sized {
+    type Type: Into<u8> + Copy;
+
+    const NAME: &'static str;
+
+    /// Given a type octet, return the associated Type if it is possible.
+    /// Returns None if the value is unrecognized.
+    fn type_from_octet(x: u8) -> Option<Self::Type>;
+
+    /// Returns length bounds for the type indicated, **including** the type
+    /// byte. Note that the assigned numbers from the Bluetooth SIG include
+    /// the type byte in their Length specifications.
+    // TODO: use impl std::ops::RangeBounds when RPITIT is sufficiently stable
+    fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8>;
+
+    /// Retrieve the type of the current value.
+    fn into_type(&self) -> Self::Type;
+
+    /// The length of the encoded value, without the length and type byte.
+    /// This cannot be 255 in practice, as the length byte is only one octet
+    /// long.
+    fn value_encoded_len(&self) -> u8;
+
+    /// Decodes the value from a buffer, which does not include the type or
+    /// length bytes. The `buf` slice length is exactly what was specified
+    /// for this value in the encoded source.
+    fn decode_value(ty: &Self::Type, buf: &[u8]) -> Result<Self, crate::packet_encoding::Error>;
+
+    /// Encodes a value into `buf`, which is verified to be the correct length
+    /// as indicated by [LtValue::value_encoded_len].
+    fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error>;
+
+    /// Decode a collection of LtValue structures that are present in a buffer.
+    /// If it is possible to continue decoding after encountering an error, does
+    /// so and includes the error. If an unrecoverable error occurs, does
+    /// not consume the final item and the last element in the result is the
+    /// error.
+    fn decode_all(buf: &[u8]) -> (Vec<Result<Self, crate::packet_encoding::Error>>, usize) {
+        let mut results = Vec::new();
+        let mut total_consumed = 0;
+        loop {
+            if buf.len() <= total_consumed {
+                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)) => {
+                    results.push(Ok(item));
+                    total_consumed += consumed;
+                }
+                Err(e @ crate::packet_encoding::Error::UnexpectedDataLength) => {
+                    results.push(Err(e));
+                    return (results, total_consumed);
+                }
+                Err(e) => {
+                    results.push(Err(e));
+                    // Consume the bytes
+                    total_consumed += indicated_len + 1;
+                }
+            }
+        }
+    }
+}
+
+impl<T: LtValue> Encodable for T {
+    type Error = crate::packet_encoding::Error;
+
+    fn encoded_len(&self) -> core::primitive::usize {
+        2 + self.value_encoded_len() as usize
+    }
+
+    fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
+        if buf.len() < self.encoded_len() {
+            return Err(crate::packet_encoding::Error::BufferTooSmall);
+        }
+        buf[0] = self.value_encoded_len() + 1;
+        buf[1] = self.into_type().into();
+        self.encode_value(&mut buf[2..self.encoded_len()])?;
+        Ok(())
+    }
+}
+
+impl<T: LtValue> Decodable for T {
+    type Error = crate::packet_encoding::Error;
+
+    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
+        if buf.len() < 2 {
+            return Err(crate::packet_encoding::Error::UnexpectedDataLength);
+        }
+        let indicated_len = buf[0] as usize;
+        if buf.len() < indicated_len + 1 {
+            return Err(crate::packet_encoding::Error::UnexpectedDataLength);
+        }
+
+        let Some(ty) = Self::type_from_octet(buf[1]) else {
+            return Err(crate::packet_encoding::Error::UnrecognizedType(
+                Self::NAME.to_owned(),
+                buf[1],
+            ));
+        };
+
+        if !Self::length_range_from_type(ty).contains(&((buf.len() - 1) as u8)) {
+            return Err(crate::packet_encoding::Error::UnexpectedDataLength);
+        }
+
+        Ok((Self::decode_value(&ty, &buf[2..=indicated_len])?, indicated_len + 1))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[derive(Copy, Clone, PartialEq, Debug)]
+    enum TestType {
+        OneByte,
+        TwoBytes,
+        TwoBytesLittleEndian,
+        UnicodeString,
+        AlwaysError,
+    }
+
+    impl From<TestType> for u8 {
+        fn from(value: TestType) -> Self {
+            match value {
+                TestType::OneByte => 1,
+                TestType::TwoBytes => 2,
+                TestType::TwoBytesLittleEndian => 3,
+                TestType::UnicodeString => 4,
+                TestType::AlwaysError => 0xFF,
+            }
+        }
+    }
+
+    #[derive(PartialEq, Debug)]
+    enum TestValues {
+        OneByte(u8),
+        TwoBytes(u16),
+        TwoBytesLittleEndian(u16),
+        UnicodeString(String),
+        AlwaysError,
+    }
+
+    impl LtValue for TestValues {
+        type Type = TestType;
+
+        const NAME: &'static str = "TestValues";
+
+        fn type_from_octet(x: u8) -> Option<Self::Type> {
+            match x {
+                1 => Some(TestType::OneByte),
+                2 => Some(TestType::TwoBytes),
+                3 => Some(TestType::TwoBytesLittleEndian),
+                4 => Some(TestType::UnicodeString),
+                0xFF => Some(TestType::AlwaysError),
+                _ => None,
+            }
+        }
+
+        fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8> {
+            match ty {
+                TestType::OneByte => 2..=2,
+                TestType::TwoBytes => 3..=3,
+                TestType::TwoBytesLittleEndian => 3..=3,
+                TestType::UnicodeString => 2..=255,
+                // AlwaysError fields can be any length (value will be thrown away)
+                TestType::AlwaysError => 1..=255,
+            }
+        }
+
+        fn into_type(&self) -> Self::Type {
+            match self {
+                TestValues::TwoBytes(_) => TestType::TwoBytes,
+                TestValues::TwoBytesLittleEndian(_) => TestType::TwoBytesLittleEndian,
+                TestValues::OneByte(_) => TestType::OneByte,
+                TestValues::UnicodeString(_) => TestType::UnicodeString,
+                TestValues::AlwaysError => TestType::AlwaysError,
+            }
+        }
+
+        fn value_encoded_len(&self) -> u8 {
+            match self {
+                TestValues::TwoBytes(_) => 2,
+                TestValues::TwoBytesLittleEndian(_) => 2,
+                TestValues::OneByte(_) => 1,
+                TestValues::UnicodeString(s) => s.len() as u8,
+                TestValues::AlwaysError => 0,
+            }
+        }
+
+        fn decode_value(
+            ty: &Self::Type,
+            buf: &[u8],
+        ) -> Result<Self, crate::packet_encoding::Error> {
+            match ty {
+                TestType::OneByte => Ok(TestValues::OneByte(buf[0])),
+                TestType::TwoBytes => {
+                    Ok(TestValues::TwoBytes(u16::from_be_bytes([buf[0], buf[1]])))
+                }
+                TestType::TwoBytesLittleEndian => {
+                    Ok(TestValues::TwoBytesLittleEndian(u16::from_le_bytes([buf[0], buf[1]])))
+                }
+                TestType::UnicodeString => {
+                    Ok(TestValues::UnicodeString(String::from_utf8_lossy(buf).into_owned()))
+                }
+                TestType::AlwaysError => Err(crate::packet_encoding::Error::OutOfRange),
+            }
+        }
+
+        fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error> {
+            match self {
+                TestValues::TwoBytes(x) => {
+                    [buf[0], buf[1]] = x.to_be_bytes();
+                }
+                TestValues::TwoBytesLittleEndian(x) => {
+                    [buf[0], buf[1]] = x.to_le_bytes();
+                }
+                TestValues::OneByte(x) => buf[0] = *x,
+                TestValues::UnicodeString(s) => {
+                    buf.copy_from_slice(s.as_bytes());
+                }
+                TestValues::AlwaysError => {
+                    return Err(crate::packet_encoding::Error::InvalidParameter("test".to_owned()));
+                }
+            }
+            Ok(())
+        }
+    }
+
+    #[test]
+    fn decode_twobytes() {
+        let encoded = [0x03, 0x02, 0x10, 0x01, 0x03, 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], Ok(TestValues::TwoBytesLittleEndian(272)));
+    }
+
+    #[test]
+    fn decode_unrecognized() {
+        let encoded = [0x03, 0x02, 0x10, 0x01, 0x03, 0x06, 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(crate::packet_encoding::Error::UnrecognizedType("TestValues".to_owned(), 6))
+        );
+    }
+
+    #[track_caller]
+    fn u8char(c: char) -> u8 {
+        c.try_into().unwrap()
+    }
+
+    #[test]
+    fn decode_variable_lengths() {
+        let encoded = [
+            0x03,
+            0x02,
+            0x10,
+            0x01,
+            0x0A,
+            0x04,
+            u8char('B'),
+            u8char('l'),
+            u8char('u'),
+            u8char('e'),
+            u8char('t'),
+            u8char('o'),
+            u8char('o'),
+            u8char('t'),
+            u8char('h'),
+            0x02,
+            0x01,
+            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], Ok(TestValues::UnicodeString("Bluetooth".to_owned())));
+        assert_eq!(decoded[2], Ok(TestValues::OneByte(1)));
+    }
+
+    #[test]
+    fn decode_with_error() {
+        let encoded = [0x03, 0x02, 0x10, 0x01, 0x02, 0xFF, 0xFF, 0x02, 0x01, 0x03];
+        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(crate::packet_encoding::Error::OutOfRange),);
+        assert_eq!(decoded[2], Ok(TestValues::OneByte(3)));
+    }
+
+    #[test]
+    fn encode_with_error() {
+        let mut buf = [0; 10];
+        let value = TestValues::AlwaysError;
+        assert!(matches!(
+            value.encode(&mut buf),
+            Err(crate::packet_encoding::Error::InvalidParameter(_)),
+        ));
+    }
+}
diff --git a/rust/bt-common/src/generic_audio.rs b/rust/bt-common/src/generic_audio.rs
index 904506c..c2f72f0 100644
--- a/rust/bt-common/src/generic_audio.rs
+++ b/rust/bt-common/src/generic_audio.rs
@@ -2,4 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+pub mod codec_capabilities;
+
 pub mod metadata_ltv;
diff --git a/rust/bt-common/src/generic_audio/codec_capabilities.rs b/rust/bt-common/src/generic_audio/codec_capabilities.rs
new file mode 100644
index 0000000..eeff6cd
--- /dev/null
+++ b/rust/bt-common/src/generic_audio/codec_capabilities.rs
@@ -0,0 +1,256 @@
+// Copyright 2023 Google LLC
+// 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 std::ops::RangeBounds;
+
+use crate::core::ltv::LtValue;
+
+#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
+pub enum CodecCapabilityType {
+    SupportedSamplingFrequencies,
+    SupportedFrameDurations,
+    SupportedAudioChannelCounts,
+    SupportedOctetsPerCodecFrame,
+    SupportedMaxCodecFramesPerSdu,
+}
+
+impl From<CodecCapabilityType> for u8 {
+    fn from(value: CodecCapabilityType) -> Self {
+        match value {
+            CodecCapabilityType::SupportedSamplingFrequencies => 1,
+            CodecCapabilityType::SupportedFrameDurations => 2,
+            CodecCapabilityType::SupportedAudioChannelCounts => 3,
+            CodecCapabilityType::SupportedOctetsPerCodecFrame => 4,
+            CodecCapabilityType::SupportedMaxCodecFramesPerSdu => 5,
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub enum FrameDurationSupport {
+    SevenFiveMs,
+    TenMs,
+    BothNoPreference,
+    PreferSevenFiveMs,
+    PreferTenMs,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub enum SamplingFrequency {
+    F8000Hz,
+    F11025Hz,
+    F16000Hz,
+    F22050Hz,
+    F24000Hz,
+    F32000Hz,
+    F44100Hz,
+    F48000Hz,
+    F88200Hz,
+    F96000Hz,
+    F176400Hz,
+    F192000Hz,
+    F384000Hz,
+}
+
+impl SamplingFrequency {
+    fn bitpos(&self) -> u8 {
+        match self {
+            SamplingFrequency::F8000Hz => 0,
+            SamplingFrequency::F11025Hz => 1,
+            SamplingFrequency::F16000Hz => 2,
+            SamplingFrequency::F22050Hz => 3,
+            SamplingFrequency::F24000Hz => 4,
+            SamplingFrequency::F32000Hz => 5,
+            SamplingFrequency::F44100Hz => 6,
+            SamplingFrequency::F48000Hz => 7,
+            SamplingFrequency::F88200Hz => 8,
+            SamplingFrequency::F96000Hz => 9,
+            SamplingFrequency::F176400Hz => 10,
+            SamplingFrequency::F192000Hz => 11,
+            SamplingFrequency::F384000Hz => 12,
+        }
+    }
+
+    fn from_bitpos(value: u8) -> Option<Self> {
+        match value {
+            0 => Some(SamplingFrequency::F8000Hz),
+            1 => Some(SamplingFrequency::F11025Hz),
+            2 => Some(SamplingFrequency::F16000Hz),
+            3 => Some(SamplingFrequency::F22050Hz),
+            4 => Some(SamplingFrequency::F24000Hz),
+            5 => Some(SamplingFrequency::F32000Hz),
+            6 => Some(SamplingFrequency::F44100Hz),
+            7 => Some(SamplingFrequency::F48000Hz),
+            8 => Some(SamplingFrequency::F88200Hz),
+            9 => Some(SamplingFrequency::F96000Hz),
+            10 => Some(SamplingFrequency::F176400Hz),
+            11 => Some(SamplingFrequency::F192000Hz),
+            12 => Some(SamplingFrequency::F384000Hz),
+            _ => None,
+        }
+    }
+}
+
+/// Codec Capability LTV Structures
+///
+/// Defined in Assigned Numbers Section 6.12.4.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum CodecCapability {
+    SupportedSamplingFrequencies(std::collections::HashSet<SamplingFrequency>),
+    SupportedFrameDurations(FrameDurationSupport),
+    SupportedAudioChannelCounts(std::collections::HashSet<u8>),
+    SupportedMaxCodecFramesPerSdu(u8),
+    SupportedOctetsPerCodecFrame { min: u16, max: u16 },
+}
+
+impl LtValue for CodecCapability {
+    type Type = CodecCapabilityType;
+
+    const NAME: &'static str = "Codec Compatability";
+
+    fn type_from_octet(x: u8) -> Option<Self::Type> {
+        match x {
+            1 => Some(CodecCapabilityType::SupportedSamplingFrequencies),
+            2 => Some(CodecCapabilityType::SupportedFrameDurations),
+            3 => Some(CodecCapabilityType::SupportedAudioChannelCounts),
+            4 => Some(CodecCapabilityType::SupportedOctetsPerCodecFrame),
+            5 => Some(CodecCapabilityType::SupportedMaxCodecFramesPerSdu),
+            _ => None,
+        }
+    }
+
+    fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8> {
+        match ty {
+            CodecCapabilityType::SupportedAudioChannelCounts => 2..=2,
+            CodecCapabilityType::SupportedFrameDurations => 2..=2,
+            CodecCapabilityType::SupportedMaxCodecFramesPerSdu => 2..=2,
+            CodecCapabilityType::SupportedOctetsPerCodecFrame => 5..=5,
+            CodecCapabilityType::SupportedSamplingFrequencies => 3..=3,
+        }
+    }
+
+    fn into_type(&self) -> Self::Type {
+        match self {
+            CodecCapability::SupportedAudioChannelCounts(_) => {
+                CodecCapabilityType::SupportedAudioChannelCounts
+            }
+            CodecCapability::SupportedFrameDurations(_) => {
+                CodecCapabilityType::SupportedFrameDurations
+            }
+            CodecCapability::SupportedMaxCodecFramesPerSdu(_) => {
+                CodecCapabilityType::SupportedMaxCodecFramesPerSdu
+            }
+            CodecCapability::SupportedOctetsPerCodecFrame { .. } => {
+                CodecCapabilityType::SupportedOctetsPerCodecFrame
+            }
+            CodecCapability::SupportedSamplingFrequencies(_) => {
+                CodecCapabilityType::SupportedSamplingFrequencies
+            }
+        }
+    }
+
+    fn value_encoded_len(&self) -> u8 {
+        // All the CodecCapabilities are constant length. Remove the type octet.
+        let range = Self::length_range_from_type(self.into_type());
+        let std::ops::Bound::Included(len) = range.start_bound() else {
+            unreachable!();
+        };
+        (len - 1).into()
+    }
+
+    fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error> {
+        match self {
+            CodecCapability::SupportedAudioChannelCounts(counts) => {
+                buf[0] = counts
+                    .iter()
+                    .fold(0, |acc, count| if *count > 8 { acc } else { acc | (1 << (*count - 1)) });
+            }
+            CodecCapability::SupportedFrameDurations(support) => {
+                buf[0] = match support {
+                    FrameDurationSupport::SevenFiveMs => 0b000001,
+                    FrameDurationSupport::TenMs => 0b000010,
+                    FrameDurationSupport::BothNoPreference => 0b000011,
+                    FrameDurationSupport::PreferSevenFiveMs => 0b010011,
+                    FrameDurationSupport::PreferTenMs => 0b100011,
+                };
+            }
+            CodecCapability::SupportedMaxCodecFramesPerSdu(max) => {
+                buf[0] = *max;
+            }
+            CodecCapability::SupportedOctetsPerCodecFrame { min, max } => {
+                let min_bytes = min.to_le_bytes();
+                buf[0..=1].copy_from_slice(&min_bytes);
+
+                let max_bytes = max.to_le_bytes();
+                buf[2..=3].copy_from_slice(&max_bytes);
+            }
+            CodecCapability::SupportedSamplingFrequencies(supported) => {
+                let sup_bytes = supported
+                    .iter()
+                    .fold(0u16, |acc, freq| acc | (1 << freq.bitpos()))
+                    .to_le_bytes();
+                buf[0..=1].copy_from_slice(&sup_bytes)
+            }
+        };
+        Ok(())
+    }
+
+    fn decode_value(
+        ty: &CodecCapabilityType,
+        buf: &[u8],
+    ) -> Result<Self, crate::packet_encoding::Error> {
+        match ty {
+            CodecCapabilityType::SupportedAudioChannelCounts => {
+                let mut supported = HashSet::new();
+                for bitpos in 0..8 {
+                    if (buf[0] & (1 << bitpos)) != 0 {
+                        supported.insert(bitpos + 1);
+                    }
+                }
+                Ok(Self::SupportedAudioChannelCounts(supported))
+            }
+            CodecCapabilityType::SupportedFrameDurations => {
+                let support = match buf[0] & 0b110011 {
+                    0b000001 => FrameDurationSupport::SevenFiveMs,
+                    0b000010 => FrameDurationSupport::TenMs,
+                    0b000011 => FrameDurationSupport::BothNoPreference,
+                    0b010011 => FrameDurationSupport::PreferSevenFiveMs,
+                    0b100011 => FrameDurationSupport::PreferTenMs,
+                    _ => {
+                        return Err(crate::packet_encoding::Error::InvalidParameter(format!(
+                            "Unrecognized bit pattern: {:#b}",
+                            buf[0]
+                        )));
+                    }
+                };
+                Ok(Self::SupportedFrameDurations(support))
+            }
+            CodecCapabilityType::SupportedMaxCodecFramesPerSdu => {
+                Ok(Self::SupportedMaxCodecFramesPerSdu(buf[0]))
+            }
+            CodecCapabilityType::SupportedOctetsPerCodecFrame => {
+                let min = u16::from_le_bytes([buf[0], buf[1]]);
+                let max = u16::from_le_bytes([buf[2], buf[3]]);
+                Ok(Self::SupportedOctetsPerCodecFrame { min, max })
+            }
+            CodecCapabilityType::SupportedSamplingFrequencies => {
+                let bitflags = u16::from_le_bytes([buf[0], buf[1]]);
+                let mut supported = HashSet::new();
+                for bitpos in 0..13 {
+                    if bitflags & (1 << bitpos) != 0 {
+                        let Some(f) = SamplingFrequency::from_bitpos(bitpos) else {
+                            continue;
+                        };
+                        let _ = supported.insert(f);
+                    }
+                }
+                Ok(Self::SupportedSamplingFrequencies(supported))
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {}
diff --git a/rust/bt-common/src/generic_audio/metadata_ltv.rs b/rust/bt-common/src/generic_audio/metadata_ltv.rs
index e29b636..3630f4e 100644
--- a/rust/bt-common/src/generic_audio/metadata_ltv.rs
+++ b/rust/bt-common/src/generic_audio/metadata_ltv.rs
@@ -31,10 +31,10 @@
         } else {
             Err(PacketError::UnexpectedDataLength)
         }
-     }
+    }
 
-     // Total size range including all Length, Type, and Value parameters.
-     fn total_size_range(&self) -> std::ops::Range<usize> {
+    // Total size range including all Length, Type, and Value parameters.
+    fn total_size_range(&self) -> std::ops::Range<usize> {
         match self {
             Type::PreferredAudioContexts | Type::StreamingAudioContexts => 4..5,
             Type::ProgramInfo | Type::CCIDList | Type::ProgramInfoURI => 2..usize::MAX,
@@ -44,7 +44,7 @@
             Type::AudioActiveState => 3..4,
             Type::BroadcastAudioImmediateRenderingFlag => 2..3,
         }
-     }
+    }
 }
 
 impl TryFrom<u8> for Type {
@@ -92,13 +92,13 @@
     BroadcastAudioImmediateRenderingFlag,
 }
 
-
 impl Metadatum {
     const LENGTH_BYTE_SIZE: usize = 1;
     const TYPE_BYTE_SIZE: usize = 1;
     const MIN_PACKET_SIZE: usize = Self::LENGTH_BYTE_SIZE + Self::TYPE_BYTE_SIZE;
 
-    // Size of this metadatum's Value parameter. Does not include the Length or Type size.
+    // Size of this metadatum's Value parameter. Does not include the Length or Type
+    // size.
     pub fn value_encoded_len(&self) -> usize {
         match self {
             Metadatum::PreferredAudioContexts(_) => 2,
@@ -124,10 +124,12 @@
             Metadatum::CCIDList(_) => Type::CCIDList,
             Metadatum::ParentalRating(_) => Type::ParentalRating,
             Metadatum::ProgramInfoURI(_) => Type::ProgramInfoURI,
-            Metadatum::ExtendedMetadata{..} => Type::ExtendedMetadata,
-            Metadatum::VendorSpecific{..} => Type::VendorSpecific,
+            Metadatum::ExtendedMetadata { .. } => Type::ExtendedMetadata,
+            Metadatum::VendorSpecific { .. } => Type::VendorSpecific,
             Metadatum::AudioActiveState(_) => Type::AudioActiveState,
-            Metadatum::BroadcastAudioImmediateRenderingFlag => Type::BroadcastAudioImmediateRenderingFlag,
+            Metadatum::BroadcastAudioImmediateRenderingFlag => {
+                Type::BroadcastAudioImmediateRenderingFlag
+            }
         }
     }
 }
@@ -148,15 +150,17 @@
         let _ = type_.length_check(total_len)?;
         let m: Metadatum = match type_ {
             Type::PreferredAudioContexts | Type::StreamingAudioContexts => {
-                let context_type = ContextType::try_from(u16::from_le_bytes(buf[2..4].try_into().unwrap()))?;
+                let context_type =
+                    ContextType::try_from(u16::from_le_bytes(buf[2..4].try_into().unwrap()))?;
                 if type_ == Type::PreferredAudioContexts {
                     Self::PreferredAudioContexts(context_type)
-                }  else {
+                } else {
                     Self::StreamingAudioContexts(context_type)
                 }
-            },
+            }
             Type::ProgramInfo | Type::ProgramInfoURI => {
-                let value = String::from_utf8(buf[2..total_len].to_vec()).map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
+                let value = String::from_utf8(buf[2..total_len].to_vec())
+                    .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
                 if type_ == Type::ProgramInfo {
                     Self::ProgramInfo(value)
                 } else {
@@ -164,55 +168,49 @@
                 }
             }
             Type::Language => {
-                let code = String::from_utf8(buf[2..total_len].to_vec()).map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
+                let code = String::from_utf8(buf[2..total_len].to_vec())
+                    .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
                 Self::LanguageCode(code)
-            },
+            }
             Type::CCIDList => Self::CCIDList(buf[2..total_len].to_vec()),
-            Type::ParentalRating => {
-                Self::ParentalRating(Rating::decode(&buf[2..3])?.0)
-            },
+            Type::ParentalRating => Self::ParentalRating(Rating::decode(&buf[2..3])?.0),
             Type::ExtendedMetadata | Type::VendorSpecific => {
                 let type_or_id = u16::from_le_bytes(buf[2..4].try_into().unwrap());
-                let data = if buf.len() >= 5 {
-                    buf[4..total_len].to_vec()
-                } else {
-                    vec![]
-                };
+                let data = if buf.len() >= 5 { buf[4..total_len].to_vec() } else { vec![] };
                 if type_ == Type::ExtendedMetadata {
-                    Self::ExtendedMetadata{
-                        type_: type_or_id,
-                        metadata: data,
-                    }
+                    Self::ExtendedMetadata { type_: type_or_id, metadata: data }
                 } else {
-                    Self::VendorSpecific{
-                        company_id: type_or_id,
-                        metadata: data,
-                    }
+                    Self::VendorSpecific { company_id: type_or_id, metadata: data }
                 }
-            },
+            }
             Type::AudioActiveState => {
                 if buf[2] > 1 {
                     return Err(PacketError::UnexpectedDataLength);
                 }
                 Self::AudioActiveState(buf[2] != 0)
-            },
-            Type::BroadcastAudioImmediateRenderingFlag => Self::BroadcastAudioImmediateRenderingFlag,
+            }
+            Type::BroadcastAudioImmediateRenderingFlag => {
+                Self::BroadcastAudioImmediateRenderingFlag
+            }
         };
-        return Ok((m, total_len))
+        return Ok((m, total_len));
     }
 }
 
 impl Encodable for Metadatum {
     type Error = PacketError;
 
-    /// Encodes the object into a provided buffer in a Metadata LTV structure. Bytes are in LSO.
-    // See Assigned Numbers section 6.12.6 for details about Metadata LTV structures.
+    /// Encodes the object into a provided buffer in a Metadata LTV structure.
+    /// Bytes are in LSO.
+    // See Assigned Numbers section 6.12.6 for details about Metadata LTV
+    // structures.
     fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
         if buf.len() < self.encoded_len() {
             return Err(PacketError::BufferTooSmall);
         }
 
-        // Length parameter value excludes the length of Length parameter itself (which is 1 byte).
+        // Length parameter value excludes the length of Length parameter itself (which
+        // is 1 byte).
         buf[0] = (self.encoded_len() - 1)
             .try_into()
             .map_err(|_| PacketError::InvalidParameter("Metadata too big".to_string()))?;
@@ -231,7 +229,7 @@
             Self::LanguageCode(value) => buf[2..5].copy_from_slice(&value.as_bytes()[..3]),
             Self::CCIDList(value) => buf[2..2 + value.len()].copy_from_slice(&value.as_slice()),
             Self::ParentalRating(value) => buf[2] = value.into(),
-            Self::ExtendedMetadata{type_, metadata} => {
+            Self::ExtendedMetadata { type_, metadata } => {
                 buf[2..4].copy_from_slice(&type_.to_le_bytes());
                 buf[4..4 + metadata.len()].copy_from_slice(&metadata.as_slice());
             }
@@ -261,7 +259,7 @@
             Metadatum::CCIDList(v) => write!(f, "CCID List: {v:?}"),
             Metadatum::ParentalRating(v) => write!(f, "Parental Rating: {v:?}"),
             Metadatum::ProgramInfoURI(v) => write!(f, "Program Info URI: {v:?}"),
-            Metadatum::ExtendedMetadata{type_, metadata} => {
+            Metadatum::ExtendedMetadata { type_, metadata } => {
                 write!(f, "Extended Metadata: type(0x{type_:02x}) metadata({metadata:?})")
             }
             Metadatum::VendorSpecific { company_id, metadata } => {
@@ -303,7 +301,6 @@
         }
         Ok(ContextType::from_bits_truncate(value))
     }
-
 }
 
 /// Represents recommended minimum age of the viewer.
@@ -337,7 +334,9 @@
 
     pub fn min_recommended(age: u8) -> Result<Self, PacketError> {
         if age < Self::MIN_RECOMMENDED_AGE {
-            return Err(PacketError::InvalidParameter(format!("minimum recommended age must be at least 5. Got {age}")));
+            return Err(PacketError::InvalidParameter(format!(
+                "minimum recommended age must be at least 5. Got {age}"
+            )));
         }
         Ok(Rating::Age(age))
     }
@@ -364,10 +363,9 @@
             0x00 => Rating::NoRating,
             0x01 => Rating::AllAge,
             value => {
-                let age = value.checked_add(Self::AGE_OFFSET)
-                    .ok_or(PacketError::OutOfRange)?;
+                let age = value.checked_add(Self::AGE_OFFSET).ok_or(PacketError::OutOfRange)?;
                 Rating::min_recommended(age).unwrap()
-            },
+            }
         };
         Ok((rating, 1))
     }
@@ -482,7 +480,7 @@
     #[test]
     fn metadatum_vendor_specific() {
         // Encoding.
-        let test = Metadatum::VendorSpecific{company_id: 0x00E0, metadata: vec![0x01, 0x02]};
+        let test = Metadatum::VendorSpecific { company_id: 0x00E0, metadata: vec![0x01, 0x02] };
         assert_eq!(test.encoded_len(), 0x06);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
diff --git a/rust/bt-common/src/lib.rs b/rust/bt-common/src/lib.rs
index 67e9360..232ec7d 100644
--- a/rust/bt-common/src/lib.rs
+++ b/rust/bt-common/src/lib.rs
@@ -4,22 +4,33 @@
 
 extern crate core as rust_core;
 
-/// Peers are identified by ids, which should be treated as opaque by service libraries.
-/// Stack implementations should ensure that each PeerId identifies a single peer over a single
-/// instance of the stack - a [`bt_gatt::Central::connect`] should always attempt to connect to the
-/// same peer as long as the PeerId was retrieved after the `Central` was instantiated.
-/// PeerIds can be valid longer than that (often if the peer is bonded)
+/// Peers are identified by ids, which should be treated as opaque by service
+/// libraries. Stack implementations should ensure that each PeerId identifies a
+/// single peer over a single instance of the stack - a
+/// [`bt_gatt::Central::connect`] should always attempt to connect to the
+/// same peer as long as the PeerId was retrieved after the `Central` was
+/// instantiated. PeerIds can be valid longer than that (often if the peer is
+/// bonded)
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct PeerId(pub u64);
 
 impl rust_core::fmt::Display for PeerId {
-    fn fmt(&self, f: &mut rust_core::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+    fn fmt(
+        &self,
+        f: &mut rust_core::fmt::Formatter<'_>,
+    ) -> std::result::Result<(), std::fmt::Error> {
         write!(f, "{:x}", self.0)
     }
 }
 
 pub mod core;
+
+pub mod company_id;
+pub use company_id::CompanyId;
+
 pub mod generic_audio;
+
 pub mod packet_encoding;
+
 pub mod uuid;
 pub use crate::uuid::Uuid;
diff --git a/rust/bt-common/src/packet_encoding.rs b/rust/bt-common/src/packet_encoding.rs
index fa01309..978430b 100644
--- a/rust/bt-common/src/packet_encoding.rs
+++ b/rust/bt-common/src/packet_encoding.rs
@@ -9,12 +9,13 @@
 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.
+    /// 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>;
 }
 
 /// An encodable type can write itself into a byte buffer.
-pub trait Encodable: ::core::marker::Sized {
+pub trait Encodable {
     type Error;
 
     /// Returns the number of bytes necessary to encode |self|.
@@ -38,4 +39,7 @@
 
     #[error("Buffer being decoded is invalid length")]
     UnexpectedDataLength,
+
+    #[error("Unrecognized type for {0}: {1}")]
+    UnrecognizedType(String, u8),
 }
diff --git a/rust/bt-common/src/uuid.rs b/rust/bt-common/src/uuid.rs
index a47f790..2f11ed8 100644
--- a/rust/bt-common/src/uuid.rs
+++ b/rust/bt-common/src/uuid.rs
@@ -7,7 +7,8 @@
 pub struct Uuid(uuid::Uuid);
 
 impl Uuid {
-    // Non-changing parts of the Bluetooth Base UUID, for easy comparison and construction.
+    // Non-changing parts of the Bluetooth Base UUID, for easy comparison and
+    // construction.
     const BASE_UUID_B_PART: u16 = 0x0000;
     const BASE_UUID_C_PART: u16 = 0x1000;
     const BASE_UUID_D_PART: [u8; 8] = [0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB];