Skip to main content

sccp_protocol/message/
values.rs

1//! Typed numeric values used by SCCP messages.
2//!
3//! Most Skinny numeric fields are extensible firmware contracts.  Data-bearing
4//! `Unknown` variants keep them type-safe without making newer phones fail to
5//! decode. Convert from the wire with `From<u32>`, inspect known values through
6//! `ALL_KNOWN`, and use `wire_value` (or `Into<u32>`) when encoding.
7
8use std::fmt;
9
10use bitflags::bitflags;
11
12use super::wire::CodecError;
13
14macro_rules! wire_enum {
15    ($(#[$meta:meta])* pub enum $name:ident { $($(#[$variant_meta:meta])* $variant:ident = $value:expr),+ $(,)? }) => {
16        $(#[$meta])*
17        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
18        pub enum $name {
19            $($(#[$variant_meta])* $variant,)+
20            /// Preserves an SCCP numeric value not recognized by this crate.
21            /// Allows newer or vendor-specific values to round-trip without loss.
22            Unknown(u32),
23        }
24
25        impl $name {
26            pub const ALL_KNOWN: &'static [Self] = &[$(Self::$variant,)+];
27
28            pub const fn wire_value(self) -> u32 {
29                match self {
30                    $(Self::$variant => $value,)+
31                    Self::Unknown(value) => value,
32                }
33            }
34
35            pub const fn is_known(self) -> bool {
36                !matches!(self, Self::Unknown(_))
37            }
38        }
39
40        impl From<u32> for $name {
41            fn from(value: u32) -> Self {
42                match value {
43                    $($value => Self::$variant,)+
44                    value => Self::Unknown(value),
45                }
46            }
47        }
48
49        impl From<$name> for u32 {
50            fn from(value: $name) -> Self {
51                value.wire_value()
52            }
53        }
54    };
55}
56
57/// A negotiated SCCP protocol version in the supported 3..=22 range.
58#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
59pub struct ProtocolVersion(u8);
60
61impl ProtocolVersion {
62    pub const MIN: Self = Self(3);
63    pub const MAX: Self = Self(22);
64    pub const V3: Self = Self(3);
65    pub const V5: Self = Self(5);
66    pub const V7: Self = Self(7);
67    pub const V8: Self = Self(8);
68    pub const V9: Self = Self(9);
69    pub const V10: Self = Self(10);
70    pub const V11: Self = Self(11);
71    pub const V12: Self = Self(12);
72    pub const V13: Self = Self(13);
73    pub const V14: Self = Self(14);
74    pub const V15: Self = Self(15);
75    pub const V16: Self = Self(16);
76    pub const V17: Self = Self(17);
77    pub const V18: Self = Self(18);
78    pub const V19: Self = Self(19);
79    pub const V20: Self = Self(20);
80    pub const V21: Self = Self(21);
81    pub const V22: Self = Self(22);
82
83    /// Validates and constructs an exact supported protocol version.
84    pub fn new(value: u32) -> Result<Self, CodecError> {
85        let value = u8::try_from(value).map_err(|_| CodecError::UnsupportedProtocol(value))?;
86        if !(Self::MIN.0..=Self::MAX.0).contains(&value) {
87            return Err(CodecError::UnsupportedProtocol(u32::from(value)));
88        }
89        Ok(Self(value))
90    }
91
92    /// Negotiate the highest version supported by both peers.
93    pub fn negotiate(advertised: u32) -> Result<Self, CodecError> {
94        if advertised < u32::from(Self::MIN.0) {
95            return Err(CodecError::UnsupportedProtocol(advertised));
96        }
97        Self::new(advertised.min(u32::from(Self::MAX.0)))
98    }
99
100    pub const fn wire(self) -> u32 {
101        self.0 as u32
102    }
103
104    /// Returns the group of version-dependent wire layouts selected by this version.
105    pub const fn layout(self) -> LayoutProfile {
106        match self.0 {
107            3..=4 => LayoutProfile::V3,
108            5..=7 => LayoutProfile::V5,
109            8..=10 => LayoutProfile::V8,
110            11..=14 => LayoutProfile::V11,
111            15 => LayoutProfile::V15,
112            16 => LayoutProfile::V16,
113            17 => LayoutProfile::V17,
114            18 => LayoutProfile::V18,
115            19..=21 => LayoutProfile::V19,
116            _ => LayoutProfile::V22,
117        }
118    }
119
120    /// General station UI messages use dynamic text layouts from version 9.
121    pub const fn uses_dynamic_general_ui(self) -> bool {
122        self.0 >= Self::V9.0
123    }
124
125    /// Returns the number of strings in this version's dynamic call-info body.
126    pub const fn dynamic_call_info_layout(self) -> DynamicCallInfoLayout {
127        match self.0 {
128            ..=15 => DynamicCallInfoLayout::Fields12,
129            16..=18 => DynamicCallInfoLayout::Fields13,
130            _ => DynamicCallInfoLayout::Fields15,
131        }
132    }
133
134    /// Reports whether dynamic speed-dial status is selected by protocol version.
135    pub const fn uses_dynamic_speed_dial_status(self) -> bool {
136        self.0 >= Self::V9.0
137    }
138}
139
140/// Version-selected shape of a dynamic call-information payload.
141#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
142pub enum DynamicCallInfoLayout {
143    Fields12,
144    Fields13,
145    Fields15,
146}
147
148impl DynamicCallInfoLayout {
149    pub const fn string_count(self) -> usize {
150        match self {
151            Self::Fields12 => 12,
152            Self::Fields13 => 13,
153            Self::Fields15 => 15,
154        }
155    }
156}
157
158impl fmt::Debug for ProtocolVersion {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        write!(f, "V{}", self.0)
161    }
162}
163
164impl fmt::Display for ProtocolVersion {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        write!(f, "v{}", self.0)
167    }
168}
169
170impl TryFrom<u32> for ProtocolVersion {
171    type Error = CodecError;
172
173    fn try_from(value: u32) -> Result<Self, Self::Error> {
174        Self::new(value)
175    }
176}
177
178impl From<ProtocolVersion> for u32 {
179    fn from(value: ProtocolVersion) -> Self {
180        value.wire()
181    }
182}
183
184/// Wire-layout transitions used between SCCP versions 3 and 22.
185#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
186pub enum LayoutProfile {
187    /// Layouts used by versions 3 and 4.
188    V3,
189    /// Layouts used by versions 5 through 7.
190    V5,
191    /// Layouts used by versions 8 through 10.
192    V8,
193    /// Layouts used by versions 11 through 14.
194    V11,
195    /// Layouts specific to version 15.
196    V15,
197    /// Layouts specific to version 16.
198    V16,
199    /// Layouts specific to version 17.
200    V17,
201    /// Layouts specific to version 18.
202    V18,
203    /// Layouts used by versions 19 through 21.
204    V19,
205    /// Layouts used by version 22.
206    V22,
207}
208
209wire_enum! {
210    /// Station device model identifier.
211    pub enum DeviceType {
212        /// No concrete device model was supplied; used as the zero/default device type.
213        Undefined = 0,
214        /// Legacy Cisco 30-button SP+ hardware station.
215        Phone30SpPlus = 1,
216        /// Legacy Cisco 12-button SP+ hardware station.
217        Phone12SpPlus = 2,
218        /// Legacy Cisco 12-button SP hardware station.
219        Phone12Sp = 3,
220        /// Legacy Cisco 12-button station without the SP feature set.
221        Phone12 = 4,
222        /// Legacy Cisco 30-button VIP hardware station.
223        Phone30Vip = 5,
224        /// Cisco Unified IP Phone 7910, an early basic SCCP desk phone.
225        Cisco7910 = 6,
226        /// Cisco Unified IP Phone 7960, a six-line SCCP desk phone.
227        Cisco7960 = 7,
228        /// Cisco Unified IP Phone 7940, a two-line SCCP desk phone.
229        Cisco7940 = 8,
230        /// Cisco Unified IP Conference Station 7935.
231        Cisco7935 = 9,
232        /// A Cisco Voice Gateway Controller phone endpoint representing an analog gateway port.
233        Vgc = 10,
234        /// Cisco ATA 186 analog telephone adapter.
235        Ata186 = 12,
236        /// Cisco ATA 188 two-port analog telephone adapter with an Ethernet pass-through port.
237        Ata188 = 13,
238        /// The virtual counterpart of the legacy 30 SP+ station, used without matching physical hardware.
239        Virtual30SpPlus = 20,
240        /// A software or application-controlled phone endpoint represented as a station device.
241        PhoneApplication = 21,
242        /// A generic analog-access gateway resource.
243        AnalogAccess = 30,
244        /// A first-generation digital-access PRI resource, historically named DigitalAccessTitan1.
245        DigitalAccessPri = 40,
246        /// A digital-access resource for a channelized T1 interface.
247        DigitalAccessT1 = 41,
248        /// A second-generation digital-access gateway resource carrying Cisco's Titan2 codename.
249        DigitalAccessTitan2 = 42,
250        /// Historical Cisco tables identify this device type as DigitalAccessLennon or WS-X6608 digital access.
251        /// The Rust variant name appears to be swapped with `DigitalAccessLennon`.
252        AnalogAccessElvis = 43,
253        /// Historical Cisco tables identify this device type as AnalogAccessElvis or WS-X6624 analog access.
254        /// The Rust variant name appears to be swapped with `AnalogAccessElvis`.
255        DigitalAccessLennon = 47,
256        /// A generic CUCM conference-bridge media resource rather than a handset.
257        ConferenceBridge = 50,
258        /// A conference-bridge implementation generation carrying Cisco's Yoko codename.
259        ConferenceBridgeYoko = 51,
260        /// A conference-bridge implementation generation carrying Cisco's Dixieland codename.
261        ConferenceBridgeDixieland = 52,
262        /// A conference-bridge implementation generation carrying Cisco's Summit codename.
263        ConferenceBridgeSummit = 53,
264        /// An H.225 call-signaling endpoint used by the H.323 stack.
265        H225 = 60,
266        /// An H.323 telephone endpoint represented in CUCM's device table.
267        H323Phone = 61,
268        /// An H.323 gateway or trunk endpoint rather than an SCCP station.
269        H323Trunk = 62,
270        /// A CUCM music-on-hold media resource.
271        MusicOnHold = 70,
272        /// A logical call-routing pilot rather than a physical endpoint.
273        Pilot = 71,
274        /// A CTI/TAPI-controlled port used by telephony applications.
275        TapiPort = 72,
276        /// A CTI/TAPI route point that applications use to receive and redirect calls.
277        TapiRoutePoint = 73,
278        /// A voicemail or voice-inbox port represented as a callable device.
279        VoiceInbox = 80,
280        /// The administrative endpoint associated with a voice-inbox service.
281        VoiceInboxAdmin = 81,
282        /// A media resource that injects announcements or call-progress prompts on a line.
283        LineAnnunciator = 82,
284        /// A software media-termination-point implementation carrying Cisco's Dixieland codename.
285        SoftwareMtpDixieland = 83,
286        /// A Cisco media-server resource providing media processing rather than station service.
287        CiscoMediaServer = 84,
288        /// A conference-bridge implementation generation carrying Cisco's Flint codename.
289        ConferenceBridgeFlint = 85,
290        /// A logical CUCM route list used to select trunks and gateways.
291        RouteList = 90,
292        /// Cisco's synthetic station type for registration and call-load testing.
293        LoadSimulator = 100,
294        /// A generic media termination point used to relay or adapt media signaling.
295        MediaTerminationPoint = 110,
296        /// A hardware media termination point carrying Cisco's Yoko generation name.
297        MediaTerminationPointYoko = 111,
298        /// A media termination point carrying Cisco's Dixieland generation name.
299        MediaTerminationPointDixieland = 112,
300        /// A media termination point carrying Cisco's Summit generation name.
301        MediaTerminationPointSummit = 113,
302        /// Cisco Unified IP Phone 7941G, a two-line programmable SCCP desk phone.
303        Cisco7941 = 115,
304        /// Cisco Unified IP Phone 7971G-GE, a color touch-screen SCCP desk phone with Gigabit Ethernet.
305        Cisco7971 = 119,
306        /// An analog station port controlled through MGCP.
307        MgcpStation = 120,
308        /// A trunk endpoint controlled through MGCP.
309        MgcpTrunk = 121,
310        /// An H.323 Registration, Admission, and Status proxy resource.
311        RasProxy = 122,
312        /// Cisco 7914 fourteen-button line expansion module attached to a compatible desk phone.
313        CiscoAddon7914 = 124,
314        /// A generic call-routing trunk whose more specific signaling family is not encoded here.
315        Trunk = 125,
316        /// A CUCM annunciator media resource that plays tones and recorded prompts.
317        Annunciator = 126,
318        /// A media bridge used to fork call audio for monitoring.
319        MonitorBridge = 127,
320        /// A recording media resource represented as a CUCM device.
321        Recorder = 128,
322        /// A monitoring bridge implementation carrying Cisco's Yoko generation name.
323        MonitorBridgeYoko = 129,
324        /// A SIP signaling trunk represented in CUCM's common device-type namespace.
325        SipTrunk = 131,
326        /// Cisco 7915 expansion module operating in its 12-button layout.
327        CiscoAddon7915_12 = 227,
328        /// Cisco 7915 expansion module operating in its 24-button layout.
329        CiscoAddon7915_24 = 228,
330        /// Cisco 7916 expansion module operating in its 12-button layout.
331        CiscoAddon7916_12 = 229,
332        /// Cisco 7916 expansion module operating in its 24-button layout.
333        CiscoAddon7916_24 = 230,
334        /// Nokia E-series mobile phone running a Cisco-compatible SCCP client.
335        NokiaESeries = 275,
336        /// Cisco Unified IP Phone 7985G desktop video phone.
337        Cisco7985 = 302,
338        /// Cisco Unified IP Phone 7911G, a basic single-line SCCP desk phone.
339        Cisco7911 = 307,
340        /// Cisco Unified IP Phone 7961G-GE, the Gigabit Ethernet six-line model.
341        Cisco7961Ge = 308,
342        /// Cisco Unified IP Phone 7941G-GE, the Gigabit Ethernet two-line model.
343        Cisco7941Ge = 309,
344        /// Cisco Unified IP Phone 7931G, a desk phone with a large set of programmable line and feature keys.
345        Cisco7931 = 348,
346        /// Cisco Unified Wireless IP Phone 7921G.
347        Cisco7921 = 365,
348        /// Cisco Unified IP Phone 7906G, a basic single-line SCCP desk phone.
349        Cisco7906 = 369,
350        /// Nokia Internet Call Client acting as an SCCP software endpoint.
351        NokiaIcc = 376,
352        /// Cisco Unified IP Phone 7962G, a six-line monochrome SCCP desk phone.
353        Cisco7962 = 404,
354        /// Cisco Unified IP Conference Station 7937G.
355        Cisco7937 = 431,
356        /// Cisco Unified IP Phone 7942G, a two-line monochrome SCCP desk phone.
357        Cisco7942 = 434,
358        /// Cisco Unified IP Phone 7945G, a two-line color SCCP desk phone with Gigabit Ethernet.
359        Cisco7945 = 435,
360        /// Cisco Unified IP Phone 7965G, a six-line color SCCP desk phone with Gigabit Ethernet.
361        Cisco7965 = 436,
362        /// Cisco Unified IP Phone 7975G, an eight-line color touch-screen SCCP desk phone.
363        Cisco7975 = 437,
364        /// Cisco Unified Wireless IP Phone 7925G.
365        Cisco7925 = 484,
366        /// Cisco Unified IP Phone 6921, a two-line entry-level desk phone.
367        Cisco6921 = 495,
368        /// Cisco Unified IP Phone 6941, a four-line desk phone.
369        Cisco6941 = 496,
370        /// Cisco Unified IP Phone 6961, a twelve-line desk phone.
371        Cisco6961 = 497,
372        /// Cisco Unified SIP Phone 6901, a displayless single-line endpoint represented in the common device table.
373        Cisco6901 = 547,
374        /// Cisco Unified IP Phone 6911, a basic single-line endpoint.
375        Cisco6911 = 548,
376        /// Cisco Unified IP Phone 6945, a four-line desk phone with Gigabit Ethernet.
377        Cisco6945 = 564,
378        /// Cisco Unified Wireless IP Phone 7926G with an integrated barcode scanner.
379        Cisco7926 = 577,
380        /// Cisco Unified IP Phone 8945, a color video desk phone with Gigabit Ethernet.
381        Cisco8945 = 585,
382        /// Cisco Unified IP Phone 8941, a color video desk phone.
383        Cisco8941 = 586,
384        /// Cisco IP Communicator, the Windows software-phone implementation of a Cisco desk phone.
385        CiscoIpCommunicator = 30016,
386        /// Cisco Unified IP Phone 7905G, a basic single-line SCCP desk phone.
387        Cisco7905 = 20000,
388        /// Cisco Wireless IP Phone 7920, the first-generation Cisco SCCP Wi-Fi handset.
389        Cisco7920 = 30002,
390        /// Cisco Unified IP Phone 7970G, an eight-line color touch-screen SCCP desk phone.
391        Cisco7970 = 30006,
392        /// Cisco Unified IP Phone 7912G, a basic single-line SCCP desk phone with an Ethernet switch.
393        Cisco7912 = 30007,
394        /// Cisco Unified IP Phone 7902G, a displayless single-line SCCP desk phone.
395        Cisco7902 = 30008,
396        /// Cisco Unified IP Phone 7961G, a six-line programmable SCCP desk phone.
397        Cisco7961 = 30018,
398        /// Cisco Unified IP Conference Station 7936.
399        Cisco7936 = 30019,
400        /// A virtual SCCP phone endpoint representing an analog gateway port.
401        AnalogGateway = 30027,
402        /// A virtual SCCP phone endpoint representing an ISDN BRI gateway port.
403        BriGateway = 30028,
404        /// Cisco SPA521S small-business IP desk phone.
405        Spa521s = 80000,
406        /// Cisco SPA524SG small-business IP desk phone with Gigabit Ethernet.
407        Spa524sg = 80001,
408        /// Cisco SPA502G one-line small-business IP desk phone.
409        Spa502g = 80003,
410        /// Cisco SPA504G four-line small-business IP desk phone.
411        Spa504g = 80004,
412        /// Cisco SPA525G five-line small-business color IP desk phone.
413        Spa525g = 80005,
414        /// Cisco SPA508G eight-line small-business IP desk phone.
415        Spa508g = 80006,
416        /// Cisco SPA509G twelve-line small-business IP desk phone.
417        Spa509g = 80007,
418        /// Second-generation Cisco SPA525G2 five-line color IP desk phone.
419        Spa525g2 = 80009,
420        /// Cisco SPA303G three-line small-business IP desk phone.
421        Spa303g = 80011,
422        /// Cisco SPA512G one-line small-business IP desk phone with Gigabit Ethernet.
423        Spa512g = 80012,
424        /// Cisco SPA514G four-line small-business IP desk phone with Gigabit Ethernet.
425        Spa514g = 80013,
426        /// Cisco SPA500S 32-button sidecar expansion module.
427        AddonSpa500s = 99991,
428        /// Cisco SPA500DS digital sidecar expansion module.
429        AddonSpa500ds = 99992,
430        /// Cisco SPA932DS attendant-console expansion module.
431        AddonSpa932ds = 99993,
432        /// Cisco's explicit “not defined” sentinel used when no registered device type applies.
433        NotDefined = 99999
434    }
435}
436
437/// Broad media class for a codec capability.
438#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
439pub enum CodecKind {
440    Audio,
441    Video,
442    Text,
443    Data,
444    TelephoneEvent,
445    Unknown,
446}
447
448wire_enum! {
449    /// Skinny payload capability / codec identifier.
450    pub enum Codec {
451        /// Indicates that no media encoding is selected or advertised.
452        None = 0x0000,
453        /// Marks an implementation-specific media format.
454        /// The identifier alone does not define an interoperable encoding.
455        NonStandard = 0x0001,
456        /// G.711 A-law PCM at 64 kbit/s, commonly used by E-carrier telephony systems.
457        Pcma = 0x0002,
458        /// G.711 A-law PCM carried through a 56 kbit/s bearer rather than a full 64 kbit/s channel.
459        G711Alaw56k = 0x0003,
460        /// G.711 µ-law PCM at 64 kbit/s, commonly used by North American and Japanese telephony systems.
461        Pcmu = 0x0004,
462        /// G.711 µ-law PCM carried through a 56 kbit/s bearer rather than a full 64 kbit/s channel.
463        G711Ulaw56k = 0x0005,
464        /// G.722 sub-band wideband speech using its 64 kbit/s mode.
465        G72264k = 0x0006,
466        /// G.722 sub-band wideband speech using its 56 kbit/s mode.
467        G72256k = 0x0007,
468        /// G.722 sub-band wideband speech using its 48 kbit/s mode.
469        G72248k = 0x0008,
470        /// G.723.1 low-bit-rate narrowband speech, normally encoded at 5.3 or 6.3 kbit/s.
471        G7231 = 0x0009,
472        /// G.728 low-delay CELP narrowband speech encoded at 16 kbit/s.
473        G728 = 0x000a,
474        /// Base G.729 CS-ACELP narrowband speech encoded at 8 kbit/s.
475        G729 = 0x000b,
476        /// The reduced-complexity Annex A profile of G.729 at 8 kbit/s.
477        G729A = 0x000c,
478        /// ISO/IEC 11172 MPEG-1 audio, retained as a legacy SCCP audio capability.
479        Is11172 = 0x000d,
480        /// ISO/IEC 13818 MPEG-2 audio, retained as a legacy SCCP audio capability.
481        Is13818 = 0x000e,
482        /// G.729 with Annex B voice-activity detection and comfort-noise generation.
483        G729B = 0x000f,
484        /// The reduced-complexity G.729 Annex A profile combined with Annex B silence suppression.
485        G729Ab = 0x0010,
486        /// GSM Full Rate cellular speech coding.
487        /// This is distinct from the RTP GSM 06.10 capability represented by `Gsm`.
488        GsmFullRate = 0x0012,
489        /// GSM Half Rate cellular speech coding, trading speech quality for reduced radio bandwidth.
490        GsmHalfRate = 0x0013,
491        /// GSM Enhanced Full Rate cellular speech coding with improved quality over GSM Full Rate.
492        GsmEnhancedFullRate = 0x0014,
493        /// Uncompressed 16-bit linear wideband PCM at 16 kHz, producing a 256 kbit/s audio stream.
494        Wideband256k = 0x0019,
495        /// A transparent 64 kbit/s data bearer rather than an audio or video encoding.
496        Data64k = 0x0020,
497        /// A transparent 56 kbit/s data bearer for rate-limited digital trunks.
498        Data56k = 0x0021,
499        /// G.722.1 wideband transform audio at 32 kbit/s, commonly known as the Siren 7 family.
500        G7221_32k = 0x0028,
501        /// G.722.1 wideband transform audio at 24 kbit/s, commonly known as the Siren 7 family.
502        G7221_24k = 0x0029,
503        /// Generic Advanced Audio Coding capability without a fixed SCCP LATM bit-rate selector.
504        Aac = 0x002a,
505        /// AAC audio transported with MPEG-4 LATM at a nominal 128 kbit/s.
506        /// Cisco capability tables associate this family with low-delay AAC.
507        Mp4aLatm128 = 0x002b,
508        /// AAC audio transported with MPEG-4 LATM at a nominal 64 kbit/s.
509        Mp4aLatm64 = 0x002c,
510        /// AAC audio transported with MPEG-4 LATM at a nominal 56 kbit/s.
511        Mp4aLatm56 = 0x002d,
512        /// AAC audio transported with MPEG-4 LATM at a nominal 48 kbit/s.
513        Mp4aLatm48 = 0x002e,
514        /// AAC audio transported with MPEG-4 LATM at a nominal 32 kbit/s.
515        Mp4aLatm32 = 0x002f,
516        /// AAC audio transported with MPEG-4 LATM at a nominal 24 kbit/s.
517        Mp4aLatm24 = 0x0030,
518        /// AAC audio transported with MPEG-4 LATM when the bit rate is negotiated elsewhere or unspecified.
519        Mp4aLatm = 0x0031,
520        /// The RTP GSM 06.10 full-rate narrowband speech format.
521        /// This is the interoperable RTP profile rather than a cellular bearer-mode selector.
522        Gsm = 0x0050,
523        /// A Cisco-defined narrowband `ActiveVoice` audio capability.
524        /// The audited SCCP sources name it but do not establish a public encoding specification.
525        ActiveVoice = 0x0051,
526        /// G.726 ADPCM narrowband speech encoded at 32 kbit/s.
527        G726_32k = 0x0052,
528        /// G.726 ADPCM narrowband speech encoded at 24 kbit/s.
529        G726_24k = 0x0053,
530        /// G.726 ADPCM narrowband speech encoded at 16 kbit/s.
531        G726_16k = 0x0054,
532        /// A later SCCP identifier for G.729 Annex B silence suppression.
533        /// It remains wire-distinct from the legacy `G729B` identifier.
534        G729AnnexB = 0x0055,
535        /// iLBC packet-loss-resilient narrowband speech, normally using 20 ms or 30 ms frames.
536        Ilbc = 0x0056,
537        /// iSAC adaptive wideband speech coding designed for variable IP-network conditions.
538        Isac = 0x0059,
539        /// Opus interactive audio, supporting speech and full-band audio with adaptive bit rate.
540        Opus = 0x005a,
541        /// Adaptive Multi-Rate narrowband cellular speech coding.
542        Amr = 0x0061,
543        /// Adaptive Multi-Rate Wideband speech coding, also standardized as G.722.2.
544        AmrWb = 0x0062,
545        /// H.261 video for audiovisual services over constant-rate digital channels.
546        H261 = 0x0064,
547        /// Base H.263 low-bit-rate video coding.
548        H263 = 0x0065,
549        /// H.263+ video, incorporating the optional enhancements standardized in H.263 version 2.
550        H263Plus = 0x0066,
551        /// Base H.264/AVC video capability.
552        H264 = 0x0067,
553        /// The scalable-video-coding extension of H.264/AVC, allowing layered temporal, spatial, or quality streams.
554        H264Svc = 0x0068,
555        /// T.120 real-time data-conferencing traffic such as shared whiteboards and application data.
556        /// It is a data capability, not a speech codec.
557        T120 = 0x0069,
558        /// H.224 low-rate data-link traffic used by applications such as H.281 far-end camera control.
559        H224 = 0x006a,
560        /// T.38 real-time facsimile relay, transporting decoded fax data instead of modem audio.
561        T38Fax = 0x006b,
562        /// A Cisco-defined `TOTE` payload capability with no public encoding semantics established here.
563        /// Available SCCP sources disagree on whether to classify it as video or data.
564        Tote = 0x006c,
565        /// H.265/HEVC video, the successor to H.264/AVC with improved compression efficiency.
566        H265 = 0x006d,
567        /// Cisco's distinct `H264_UC` video capability.
568        /// The audited sources do not define what the UC profile adds, so it is not treated as base H.264.
569        H264Uc = 0x006e,
570        /// Cisco X-V.150 modem relay associated with a G.711 µ-law voiceband-data path.
571        /// Legacy chan-sccp sources identify it with modem traffic on VG224 gateways.
572        Xv150ModemRelay711u = 0x006f,
573        /// Cisco named-signaling-event mode for voiceband data carried over G.711 µ-law.
574        NseVbd711u = 0x0070,
575        /// Cisco X-V.150 modem relay associated with a G.729 Annex A voice path.
576        /// Legacy chan-sccp sources identify it with modem traffic on VG224 gateways.
577        Xv150ModemRelay729a = 0x0071,
578        /// Cisco named-signaling-event mode for voiceband data associated with G.729 Annex A.
579        NseVbd729a = 0x0072,
580        /// Cisco's H.264 capability variant carrying forward-error-correction support.
581        /// It is negotiated separately because the backend has no equivalent base-H.264 flag.
582        H264Fec = 0x0073,
583        /// A transparent clear-channel bearer that preserves arbitrary digital data without transcoding.
584        ClearChannel = 0x0078,
585        /// A Cisco media-resource capability representing a universal transcoder.
586        /// It identifies a transformation service rather than a media encoding.
587        UniversalTranscoder = 0x00de,
588        /// DTMF carried as RTP `telephone-event` packets using a dynamically negotiated payload type.
589        /// The historical name references RFC 2833; RFC 4733 later replaced it.
590        DtmfOutOfBandRfc2833 = 0x0101,
591        /// Cisco's proprietary RTP DTMF passthrough payload rather than audible in-band tones.
592        DtmfPassthrough = 0x0102,
593        /// A Cisco DTMF event capability whose RTP payload number is negotiated dynamically.
594        DtmfDynamic = 0x0103,
595        /// Cisco out-of-band DTMF signaling, carrying digits separately from the voice samples.
596        DtmfOutOfBand = 0x0104,
597        /// Cisco's historical “in-band RFC 2833” event-payload mode.
598        /// Despite the label, it represents packetized digit events rather than acoustic DTMF audio.
599        DtmfInBandRfc2833 = 0x0105,
600        /// Conference-bridge tone events used by Cisco media resources.
601        /// This is a control/event payload, not an audio codec.
602        CfbTones = 0x0106,
603        /// DTMF event signaling without a companion audio stream.
604        DtmfNoAudio = 0x012b,
605        /// The V.150.1 modem-relay media mode, carrying demodulated modem data across an IP network.
606        V150ModemRelay = 0x012c,
607        /// The V.150.1 Simple Packet Relay Transport used for reliable modem-relay data.
608        V150Sprt = 0x012d,
609        /// The V.150.1 State Signalling Events channel used to coordinate transitions among audio, VBD, and relay modes.
610        V150Sse = 0x012e
611    }
612}
613
614impl Codec {
615    /// Backward-compatible name for the SCCP numeric value.
616    pub const fn skinny(self) -> u32 {
617        self.wire_value()
618    }
619
620    /// Classifies this capability into its broad media family.
621    pub const fn kind(self) -> CodecKind {
622        match self {
623            Self::H261
624            | Self::H263
625            | Self::H263Plus
626            | Self::H264
627            | Self::H264Svc
628            | Self::H265
629            | Self::H264Uc
630            | Self::H264Fec => CodecKind::Video,
631            Self::T120 | Self::H224 => CodecKind::Text,
632            Self::Data64k
633            | Self::Data56k
634            | Self::T38Fax
635            | Self::Tote
636            | Self::Xv150ModemRelay711u
637            | Self::NseVbd711u
638            | Self::Xv150ModemRelay729a
639            | Self::NseVbd729a
640            | Self::ClearChannel
641            | Self::UniversalTranscoder
642            | Self::V150ModemRelay
643            | Self::V150Sprt
644            | Self::V150Sse => CodecKind::Data,
645            Self::DtmfOutOfBandRfc2833
646            | Self::DtmfPassthrough
647            | Self::DtmfDynamic
648            | Self::DtmfOutOfBand
649            | Self::DtmfInBandRfc2833
650            | Self::DtmfNoAudio
651            | Self::CfbTones => CodecKind::TelephoneEvent,
652            Self::None | Self::NonStandard | Self::Unknown(_) => CodecKind::Unknown,
653            _ => CodecKind::Audio,
654        }
655    }
656
657    /// Returns the nominal clock rate in hertz for supported audio codecs.
658    ///
659    /// Non-audio, unrecognized, and codecs without a defined mapping return
660    /// `None`.
661    pub const fn sample_rate(self) -> Option<u32> {
662        match self {
663            Self::G72264k
664            | Self::G72256k
665            | Self::G72248k
666            | Self::G7221_32k
667            | Self::G7221_24k
668            | Self::Wideband256k
669            | Self::AmrWb => Some(16_000),
670            Self::Opus | Self::Isac => Some(48_000),
671            codec if matches!(codec.kind(), CodecKind::Audio) => Some(8_000),
672            _ => None,
673        }
674    }
675
676    /// Returns the codec's default RTP payload type when statically assigned.
677    ///
678    /// Dynamically assigned codecs return `None` and require negotiated
679    /// payload metadata.
680    pub const fn rtp_payload_type(self) -> Option<u8> {
681        match self {
682            Self::Pcmu | Self::G711Ulaw56k => Some(0),
683            Self::Gsm => Some(3),
684            Self::G7231 => Some(4),
685            Self::Pcma | Self::G711Alaw56k => Some(8),
686            Self::G72264k | Self::G72256k | Self::G72248k => Some(9),
687            Self::G729 | Self::G729A | Self::G729B | Self::G729Ab | Self::G729AnnexB => Some(18),
688            Self::Wideband256k => Some(25),
689            Self::Ilbc => Some(97),
690            Self::G7221_32k => Some(102),
691            Self::Opus => Some(107),
692            Self::G726_32k => Some(112),
693            _ => None,
694        }
695    }
696}
697
698wire_enum! {
699    /// Station call-state indication shown by the call plane.
700    pub enum CallState {
701        OffHook = 1,
702        OnHook = 2,
703        RingOut = 3,
704        RingIn = 4,
705        Connected = 5,
706        Busy = 6,
707        Congestion = 7,
708        Hold = 8,
709        CallWaiting = 9,
710        Transfer = 10,
711        Park = 11,
712        Proceed = 12,
713        RemoteMultiline = 13,
714        InvalidNumber = 14,
715        HoldYellow = 15,
716        IntercomOneWay = 16,
717        HoldRed = 17
718    }
719}
720
721wire_enum! {
722    /// Direction and origin classification attached to call information.
723    pub enum CallType {
724        Inbound = 1,
725        Outbound = 2,
726        Forward = 3
727    }
728}
729
730wire_enum! {
731    /// Operational severity attached to a station alarm report.
732    pub enum AlarmSeverity {
733        Critical = 0,
734        Warning = 1,
735        Informational = 2,
736        ProtocolUnknown = 4,
737        Major = 7,
738        Minor = 8,
739        Marginal = 10,
740        TraceInfo = 20
741    }
742}
743
744wire_enum! {
745    /// Result status returned by media-channel operations.
746    pub enum MediaStatus {
747        Ok = 0,
748        UnspecifiedError = 1,
749        OutOfChannels = 2,
750        CodecTooComplex = 3,
751        InvalidPartyId = 4,
752        InvalidCallReference = 5,
753        InvalidCodec = 6,
754        InvalidPacketSize = 7,
755        OutOfSockets = 8,
756        EncoderOrDecoderFailed = 9,
757        InvalidDynamicPayload = 10,
758        RequestedAddressTypeUnavailable = 11,
759        DeviceOnHook = 12
760    }
761}
762
763wire_enum! {
764    /// Physical or logical button stimulus reported by a station.
765    pub enum Stimulus {
766        /// Reports an unassigned or inactive station control; no feature action is implied.
767        Unused = 0x00,
768        /// Reports use of the redial control to call the most recently dialed destination.
769        LastNumberRedial = 0x01,
770        /// Reports activation of a provisioned speed-dial entry.
771        SpeedDial = 0x02,
772        /// Reports use of the hold control for the addressed call.
773        Hold = 0x03,
774        /// Reports use of the transfer control to start or complete a call transfer.
775        Transfer = 0x04,
776        /// Reports use of the call-forward-all control.
777        ForwardAll = 0x05,
778        /// Reports use of the call-forward-on-busy control.
779        ForwardBusy = 0x06,
780        /// Reports use of the call-forward-on-no-answer control.
781        ForwardNoAnswer = 0x07,
782        /// Reports activation of a legacy display-oriented station control.
783        Display = 0x08,
784        /// Reports selection of a line appearance or a call on that appearance.
785        Line = 0x09,
786        /// Reports activation of the T.120 text-chat application.
787        T120Chat = 0x0a,
788        /// Reports activation of the T.120 shared-whiteboard application.
789        T120Whiteboard = 0x0b,
790        /// Reports activation of T.120 application sharing.
791        T120ApplicationSharing = 0x0c,
792        /// Reports activation of T.120 conference file transfer.
793        T120FileTransfer = 0x0d,
794        /// Reports use of the station's video control.
795        Video = 0x0e,
796        /// Reports activation of a voicemail access key.
797        Voicemail = 0x0f,
798        /// Reports use of a combined answer/release control.
799        AnswerRelease = 0x10,
800        /// Reports use of the station's automatic-answer control.
801        AutoAnswer = 0x11,
802        /// Reports use of the call-selection control for multi-call operations.
803        Select = 0x12,
804        /// Reports use of the call-privacy control.
805        Privacy = 0x13,
806        /// Reports activation of a provisioned phone-service URL.
807        ServiceUrl = 0x14,
808        /// Reports activation of a speed dial that also monitors its target through BLF.
809        BlfSpeedDial = 0x15,
810        /// Reports a directed-park request targeting a specific park destination.
811        DirectedPark = 0x16,
812        /// Reports activation of an intercom appearance or intercom call.
813        Intercom = 0x17,
814        /// Reports use of the malicious-call identification feature.
815        MaliciousCall = 0x1b,
816        /// Reports application-defined programmable control B1; SCCP assigns no universal action.
817        GenericAppB1 = 0x21,
818        /// Reports application-defined programmable control B2; SCCP assigns no universal action.
819        GenericAppB2 = 0x22,
820        /// Reports application-defined programmable control B3; SCCP assigns no universal action.
821        GenericAppB3 = 0x23,
822        /// Reports application-defined programmable control B4; SCCP assigns no universal action.
823        GenericAppB4 = 0x24,
824        /// Reports application-defined programmable control B5; SCCP assigns no universal action.
825        GenericAppB5 = 0x25,
826        /// Reports activation of a generic feature whose lamp can expose multiple blink states.
827        MultiblinkFeature = 0x26,
828        /// Reports activation of the dial-in Meet-Me conference workflow.
829        MeetMeConference = 0x7b,
830        /// Reports use of the station conference control for an active call.
831        Conference = 0x7d,
832        /// Reports a request to park the current call.
833        CallPark = 0x7e,
834        /// Reports a request to answer a ringing call in the configured pickup scope.
835        CallPickup = 0x7f,
836        /// Reports a group-pickup request for a ringing call in another pickup group.
837        GroupCallPickup = 0x80,
838        /// Reports activation of the extension-mobility login or logout feature.
839        Mobility = 0x81,
840        /// Reports use of the do-not-disturb control.
841        DoNotDisturb = 0x82,
842        /// Reports a request to show or operate on the conference participant list.
843        ConferenceList = 0x83,
844        /// Reports a request to remove the most recently added conference participant.
845        RemoveLastParticipant = 0x84,
846        /// Reports activation of Cisco's call-quality reporting tool.
847        QualityReportTool = 0x85,
848        /// Reports a callback request for a busy or unavailable destination.
849        Callback = 0x86,
850        /// Reports use of Cisco's alternate call-pickup feature.
851        OtherPickup = 0x87,
852        /// Reports a request to change the call's video mode.
853        VideoMode = 0x88,
854        /// Reports use of the new-call control to allocate an outbound call.
855        NewCall = 0x89,
856        /// Reports use of the end-call control to release the addressed call.
857        EndCall = 0x8a,
858        /// Reports a hunt-group login or logout request.
859        HuntGroupLogin = 0x8b,
860        /// Reports activation of Cisco's call-queuing feature.
861        Queuing = 0x8f,
862        /// Reports activation of a parking-lot view or retrieval workflow.
863        ParkingLot = 0xc0,
864        /// Reports use of the station's fixed Messages key.
865        Messages = 0xc2,
866        /// Reports use of the station's fixed Directories key.
867        Directory = 0xc3,
868        /// Reports use of the station's fixed Applications or Services key.
869        Application = 0xc5,
870        /// Reports a headset-control state change from the station.
871        Headset = 0xc6,
872        /// Identifies input originating from the station's physical keypad control block.
873        Keypad = 0xf0,
874        /// Reports use of the station acoustic-echo-cancellation control.
875        AcousticEchoCancellation = 0xfd,
876        /// Preserves a station stimulus that Cisco marks as undefined rather than unassigned.
877        Undefined = 0xff
878    }
879}
880
881wire_enum! {
882    /// Soft-key events are one-based positions in the advertised template.
883    pub enum SoftKey {
884        Redial = 1,
885        NewCall = 2,
886        Hold = 3,
887        Transfer = 4,
888        ForwardAll = 5,
889        ForwardBusy = 6,
890        ForwardNoAnswer = 7,
891        Backspace = 8,
892        EndCall = 9,
893        Resume = 10,
894        Answer = 11,
895        Info = 12,
896        Conference = 13,
897        Park = 14,
898        Join = 15,
899        MeetMe = 16,
900        Pickup = 17,
901        GroupPickup = 18,
902        Monitor = 19,
903        Callback = 20,
904        Barge = 21,
905        DoNotDisturb = 22,
906        ConferenceList = 23,
907        Select = 24,
908        Private = 25,
909        TransferToVoicemail = 26,
910        DirectTransfer = 27,
911        ImmediateDivert = 28,
912        VideoMode = 29,
913        Intercept = 30,
914        Empty = 31,
915        Dial = 32
916    }
917}
918
919wire_enum! {
920    /// Call-state context used to select an advertised soft-key set.
921    pub enum KeyMode {
922        OnHook = 0,
923        Connected = 1,
924        OnHold = 2,
925        RingIn = 3,
926        OffHook = 4,
927        ConnectedTransfer = 5,
928        DigitsFollowing = 6,
929        ConnectedConference = 7,
930        RingOut = 8,
931        OffHookFeature = 9,
932        InUseHint = 10,
933        OnHookStealable = 11,
934        HoldConference = 12,
935        Empty = 13
936    }
937}
938
939wire_enum! {
940    /// Audible ringer pattern selected for the station.
941    pub enum RingerMode {
942        Off = 1,
943        Inside = 2,
944        Outside = 3,
945        Feature = 4,
946        Silent = 5,
947        Urgent = 6,
948        Bellcore1 = 7,
949        Bellcore2 = 8,
950        Bellcore3 = 9,
951        Bellcore4 = 10,
952        Bellcore5 = 11
953    }
954}
955
956wire_enum! {
957    /// Whether a ringer command applies once or continuously.
958    pub enum RingDuration {
959        Normal = 1,
960        Single = 2
961    }
962}
963
964wire_enum! {
965    /// Visual state selected for a station lamp.
966    pub enum LampMode {
967        Off = 1,
968        On = 2,
969        Wink = 3,
970        Flash = 4,
971        Blink = 5,
972        Hold = 6,
973        Ring = 7,
974        Custom1 = 8,
975        Custom2 = 9
976    }
977}
978
979wire_enum! {
980    /// Tone identifier used by tone and announcement commands.
981    pub enum Tone {
982        /// Stops any current station tone; the server uses this as the explicit silent tone request.
983        Silence = 0x00,
984        /// The DTMF `1` signal: 697 Hz row tone combined with a 1,209 Hz column tone.
985        Dtmf1 = 0x01,
986        /// The DTMF `2` signal: 697 Hz row tone combined with a 1,336 Hz column tone.
987        Dtmf2 = 0x02,
988        /// The DTMF `3` signal: 697 Hz row tone combined with a 1,477 Hz column tone.
989        Dtmf3 = 0x03,
990        /// The DTMF `4` signal: 770 Hz row tone combined with a 1,209 Hz column tone.
991        Dtmf4 = 0x04,
992        /// The DTMF `5` signal: 770 Hz row tone combined with a 1,336 Hz column tone.
993        Dtmf5 = 0x05,
994        /// The DTMF `6` signal: 770 Hz row tone combined with a 1,477 Hz column tone.
995        Dtmf6 = 0x06,
996        /// The DTMF `7` signal: 852 Hz row tone combined with a 1,209 Hz column tone.
997        Dtmf7 = 0x07,
998        /// The DTMF `8` signal: 852 Hz row tone combined with a 1,336 Hz column tone.
999        Dtmf8 = 0x08,
1000        /// The DTMF `9` signal: 852 Hz row tone combined with a 1,477 Hz column tone.
1001        Dtmf9 = 0x09,
1002        /// The DTMF `0` signal: 941 Hz row tone combined with a 1,336 Hz column tone.
1003        Dtmf0 = 0x0a,
1004        /// The DTMF `*` signal: 941 Hz row tone combined with a 1,209 Hz column tone.
1005        DtmfStar = 0x0e,
1006        /// The DTMF `#` signal: 941 Hz row tone combined with a 1,477 Hz column tone.
1007        DtmfPound = 0x0f,
1008        /// The DTMF `A` signal from the fourth keypad column used by AUTOVON and control systems.
1009        /// Combines a 697 Hz row tone with a 1,633 Hz column tone.
1010        DtmfA = 0x10,
1011        /// The DTMF `B` signal from the fourth keypad column used by AUTOVON and control systems.
1012        /// Combines a 770 Hz row tone with a 1,633 Hz column tone.
1013        DtmfB = 0x11,
1014        /// The DTMF `C` signal from the fourth keypad column used by AUTOVON and control systems.
1015        /// Combines an 852 Hz row tone with a 1,633 Hz column tone.
1016        DtmfC = 0x12,
1017        /// The DTMF `D` signal from the fourth keypad column used by AUTOVON and control systems.
1018        /// Combines a 941 Hz row tone with a 1,633 Hz column tone.
1019        DtmfD = 0x13,
1020        /// Dial tone indicating that the caller may dial an internal extension.
1021        InsideDial = 0x21,
1022        /// Dial tone indicating access to an external or public telephone network.
1023        OutsideDial = 0x22,
1024        /// Busy tone indicating that the called line cannot accept the call.
1025        LineBusy = 0x23,
1026        /// Audible ringback indicating that the remote endpoint is being alerted.
1027        Alerting = 0x24,
1028        /// Fast-busy or reorder tone indicating congestion or an unusable dialing sequence.
1029        Reorder = 0x25,
1030        /// Periodic warning beep informing participants that the call is being recorded.
1031        RecorderWarning = 0x26,
1032        /// Station feedback indicating that a recording device or recording service was detected.
1033        RecorderDetected = 0x27,
1034        /// Recall tone used when a held, parked, or transferred call reverts to the station.
1035        Reverting = 0x28,
1036        /// Loud off-hook warning tone played when the handset remains off hook without a call.
1037        ReceiverOffHook = 0x29,
1038        /// Intercept tone indicating that the supplied address or digit sequence is incomplete.
1039        PartialDial = 0x2a,
1040        /// Intercept tone indicating that the dialed number does not exist.
1041        NoSuchNumber = 0x2b,
1042        /// Special tone used while an operator performs busy-line verification.
1043        BusyVerification = 0x2c,
1044        /// Brief in-call alert indicating that another call is waiting.
1045        CallWaiting = 0x2d,
1046        /// Positive confirmation tone indicating that a requested feature was accepted.
1047        Confirmation = 0x2e,
1048        /// Tone indicating that the call is camped on a busy destination awaiting availability.
1049        CampOn = 0x2f,
1050        /// Dial tone presented after recall or hook flash so the caller can enter another destination.
1051        RecallDial = 0x30,
1052        /// Cisco's two-part “zip-zip” feature alert.
1053        /// The exact user-facing meaning depends on the call feature that requests it.
1054        ZipZip = 0x31,
1055        /// Cisco's short “zip” feature alert.
1056        /// The exact user-facing meaning depends on the call feature that requests it.
1057        Zip = 0x32,
1058        /// Cisco's paired positive/negative feature-feedback sound.
1059        BeepBonk = 0x33,
1060        /// Requests the station's built-in music tone rather than an RTP music-on-hold stream.
1061        Music = 0x34,
1062        /// Audible indication associated with placing or leaving a call on hold.
1063        Hold = 0x35,
1064        /// A station test tone used for diagnostics rather than normal call progress.
1065        Test = 0x36,
1066        /// Warning tone indicating that the call is being monitored.
1067        MonitorWarning = 0x37,
1068        /// Call-waiting alert used when another waiting call is added.
1069        AddCallWaiting = 0x40,
1070        /// Higher-priority call-waiting alert for precedence-aware call handling.
1071        PriorityCallWaiting = 0x41,
1072        /// Warning tone indicating that another party has barged into the call.
1073        BargeIn = 0x43,
1074        /// Distinctive alerting cadence used to distinguish a call class from normal ringing.
1075        DistinctAlert = 0x44,
1076        /// Priority alerting cadence used for a higher-precedence incoming call.
1077        PriorityAlert = 0x45,
1078        /// Short reminder ring for a held, parked, forwarded, or otherwise pending call.
1079        ReminderRing = 0x46,
1080        /// Ringback used by Multilevel Precedence and Preemption calls.
1081        PrecedenceRingback = 0x47,
1082        /// Warning tone indicating that a lower-precedence call is being preempted.
1083        Preemption = 0x48,
1084        /// Sentinel indicating that no call-progress tone is assigned.
1085        /// Unlike `Silence`, it does not request an active silence tone.
1086        NoTone = 0x7f,
1087        /// Conference-service greeting played when entering a Meet-Me flow.
1088        MeetMeGreeting = 0x80,
1089        /// Conference prompt indicating that the entered Meet-Me number is invalid.
1090        MeetMeNumberInvalid = 0x81,
1091        /// Conference prompt indicating that the entered Meet-Me number could not be used.
1092        MeetMeNumberFailed = 0x82,
1093        /// Conference prompt requesting the participant PIN.
1094        MeetMeEnterPin = 0x83,
1095        /// Conference prompt indicating that the participant PIN is invalid.
1096        MeetMeInvalidPin = 0x84,
1097        /// Conference prompt indicating that PIN validation failed.
1098        MeetMeFailedPin = 0x85,
1099        /// Conference prompt indicating that allocation of the conference bridge failed.
1100        MeetMeCfbFailed = 0x86,
1101        /// Conference prompt requesting an access code.
1102        MeetMeEnterAccessCode = 0x87,
1103        /// Conference prompt indicating that the supplied access code is invalid.
1104        MeetMeAccessCodeInvalid = 0x88,
1105        /// Conference prompt indicating that access-code validation failed.
1106        MeetMeAccessCodeFailed = 0x89
1107    }
1108}
1109
1110wire_enum! {
1111    /// Media direction in which a station should play a tone.
1112    pub enum ToneDirection {
1113        User = 0,
1114        Network = 1,
1115        Both = 2
1116    }
1117}
1118
1119wire_enum! {
1120    /// Station audio-path component named by a media-path event.
1121    pub enum MediaPathId {
1122        None = 0,
1123        Headset = 1,
1124        Handset = 2,
1125        Speaker = 3
1126    }
1127}
1128
1129wire_enum! {
1130    /// Availability transition reported for a station media path.
1131    pub enum MediaPathEvent {
1132        None = 0,
1133        On = 1,
1134        Off = 2
1135    }
1136}
1137
1138wire_enum! {
1139    /// Capability state reported for a station media path.
1140    pub enum MediaPathCapability {
1141        None = 0,
1142        Enable = 1,
1143        Disable = 2,
1144        Monitor = 3
1145    }
1146}
1147
1148wire_enum! {
1149    /// Media class used when allocating or closing ports.
1150    pub enum MediaType {
1151        Invalid = 0,
1152        Audio = 1,
1153        MainVideo = 2,
1154        Fecc = 3,
1155        PresentationVideo = 4,
1156        Bfcp = 5,
1157        IxChannel = 6,
1158        T38 = 7
1159    }
1160}
1161
1162wire_enum! {
1163    /// Transport family requested for a media endpoint.
1164    pub enum MediaTransport {
1165        Rtp = 1,
1166        Udp = 2,
1167        Tcp = 3
1168    }
1169}
1170
1171wire_enum! {
1172    /// RSVP reservation direction carried by SCCP QoS service messages.
1173    pub enum QosDirection {
1174        Send = 1,
1175        Receive = 2,
1176        SendReceive = 3
1177    }
1178}
1179
1180wire_enum! {
1181    /// RSVP reservation style used by QoS path setup.
1182    pub enum QosReservationStyle {
1183        FixedFilter = 1,
1184        SharedExplicit = 2,
1185        WildcardFilter = 3
1186    }
1187}
1188
1189wire_enum! {
1190    /// QoS service failure reported independently of RSVP protocol errors.
1191    pub enum QosErrorCode {
1192        ReservationTimeout = 0,
1193        PathFailed = 1,
1194        ReservationFailed = 2,
1195        ListenFailed = 3,
1196        ResourceUnavailable = 4,
1197        ListenTimeout = 5,
1198        ReservationRetriesFailed = 6,
1199        PathRetriesFailed = 7,
1200        ReservationPreempted = 8,
1201        PathPreempted = 9,
1202        ReservationModifyFailed = 10,
1203        PathModifyFailed = 11,
1204        ReservationTornDown = 12
1205    }
1206}
1207
1208wire_enum! {
1209    /// RSVP protocol error returned by a failed QoS reservation.
1210    pub enum RsvpErrorCode {
1211        Confirm = 0,
1212        Admission = 1,
1213        Administrative = 2,
1214        NoPathInformation = 3,
1215        NoSenderInformation = 4,
1216        ConflictingStyle = 5,
1217        UnknownStyle = 6,
1218        ConflictingDestinationPorts = 7,
1219        ConflictingSourcePorts = 8,
1220        ServicePreempted = 12,
1221        UnknownObjectClass = 13,
1222        UnknownClassType = 14,
1223        Api = 20,
1224        Traffic = 21,
1225        TrafficSystem = 22,
1226        System = 23,
1227        RoutingProblem = 24
1228    }
1229}
1230
1231wire_enum! {
1232    /// Requested acknowledgement behavior at the end of an announcement.
1233    pub enum EndOfAnnouncementAck {
1234        NotRequired = 0,
1235        Required = 1
1236    }
1237}
1238
1239wire_enum! {
1240    /// Ordering policy for playing an announcement sequence.
1241    pub enum AnnouncementPlayMode {
1242        XmlConfigured = 0,
1243        OneShot = 1,
1244        Continuous = 2
1245    }
1246}
1247
1248wire_enum! {
1249    /// Completion status returned when announcement playback finishes.
1250    pub enum AnnouncementPlayStatus {
1251        Ok = 0,
1252        Error = 1
1253    }
1254}
1255
1256wire_enum! {
1257    /// Result returned for a message-waiting notification.
1258    pub enum MessageWaitingResult {
1259        Ok = 0,
1260        GeneralError = 1,
1261        RequestRejected = 2,
1262        VoicemailCountOutOfBounds = 3,
1263        FaxCountOutOfBounds = 4,
1264        InvalidPriorityVoicemailCount = 5,
1265        InvalidPriorityFaxCount = 6
1266    }
1267}
1268
1269wire_enum! {
1270    /// Whether a connection-statistics response clears the station counters.
1271    pub enum StatisticsProcessing {
1272        Clear = 0,
1273        DoNotClear = 1
1274    }
1275}
1276
1277wire_enum! {
1278    /// Network-address family selected by a versioned media layout.
1279    pub enum IpAddressType {
1280        Ipv4 = 0,
1281        Ipv6 = 1,
1282        Ipv4AndIpv6 = 2,
1283        Invalid = 3
1284    }
1285}
1286
1287wire_enum! {
1288    /// Restart scope requested from a station.
1289    pub enum ResetType {
1290        Reset = 1,
1291        Restart = 2,
1292        ApplyConfiguration = 3
1293    }
1294}
1295
1296wire_enum! {
1297    /// Policy used to transport connected-call DTMF digits.
1298    pub enum DtmfMode {
1299        Auto = 0,
1300        Rfc2833 = 1,
1301        Skinny = 2
1302    }
1303}
1304
1305wire_enum! {
1306    /// Call-forwarding condition represented by a forwarding entry.
1307    pub enum CallForwardKind {
1308        None = 0,
1309        All = 1,
1310        Busy = 2,
1311        NoAnswer = 3
1312    }
1313}
1314
1315wire_enum! {
1316    /// Precedence assigned to a call or media request.
1317    pub enum CallPriority {
1318        Highest = 0,
1319        High = 1,
1320        Medium = 2,
1321        Low = 3,
1322        Normal = 4
1323    }
1324}
1325
1326wire_enum! {
1327    /// Ordered status-line notification slots. Larger values take precedence.
1328    pub enum NotificationPriority {
1329        Idle = 0,
1330        Voicemail = 1,
1331        Monitor = 2,
1332        Privacy = 3,
1333        DoNotDisturb = 4,
1334        CallForward = 5,
1335        Timed = 6
1336    }
1337}
1338
1339wire_enum! {
1340    /// Visibility policy for call-information presentation.
1341    pub enum CallInfoVisibility {
1342        Default = 0,
1343        Collapsed = 1,
1344        Hidden = 2
1345    }
1346}
1347
1348wire_enum! {
1349    /// Security indication presented for a call.
1350    pub enum CallSecurityState {
1351        UnknownState = 0,
1352        NotAuthenticated = 1,
1353        Authenticated = 2
1354    }
1355}
1356
1357wire_enum! {
1358    /// Busy-lamp-field availability reported by a subscription notification.
1359    pub enum BusyLampFieldState {
1360        UnknownState = 0,
1361        Idle = 1,
1362        InUse = 2,
1363        DoNotDisturb = 3,
1364        Alerting = 4
1365    }
1366}
1367
1368wire_enum! {
1369    /// Result of a phone-book/BLF subscription request.
1370    pub enum SubscriptionCause {
1371        Ok = 0,
1372        RouteFailure = 1,
1373        AuthenticationFailure = 2,
1374        Timeout = 3,
1375        TrunkTerminated = 4,
1376        TrunkForbidden = 5,
1377        Throttled = 6
1378    }
1379}
1380
1381wire_enum! {
1382    /// Picture-size profile used by a video capability.
1383    pub enum VideoFormat {
1384        Undefined = 0,
1385        Sqcif = 1,
1386        Qcif = 2,
1387        Cif = 3,
1388        Cif4 = 4,
1389        Cif16 = 5,
1390        Custom = 6,
1391        ProtocolUnknown = 232
1392    }
1393}
1394
1395wire_enum! {
1396    /// Codec-specific operation carried by a miscellaneous multimedia command.
1397    pub enum MiscCommandType {
1398        VideoFreezePicture = 0,
1399        VideoFastUpdatePicture = 1,
1400        VideoFastUpdateGob = 2,
1401        VideoFastUpdateMacroblock = 3,
1402        LostPicture = 4,
1403        LostPartialPicture = 5,
1404        RecoveryReferencePicture = 6,
1405        TemporalSpatialTradeoff = 7
1406    }
1407}
1408
1409wire_enum! {
1410    /// Station echo-cancellation policy for an audio channel.
1411    pub enum EchoCancellation {
1412        Off = 0,
1413        On = 1
1414    }
1415}
1416
1417wire_enum! {
1418    /// Station-side voice-activity detection/silence suppression policy.
1419    pub enum SilenceSuppression {
1420        Off = 0,
1421        On = 1
1422    }
1423}
1424
1425wire_enum! {
1426    /// Bit-rate selector occupying the codec qualifier word for G.723.
1427    pub enum G723BitRate {
1428        Rate5_3 = 1,
1429        Rate6_3 = 2
1430    }
1431}
1432
1433wire_enum! {
1434    /// Station result attached to an unregister acknowledgement.
1435    pub enum UnregisterStatus {
1436        Ok = 0,
1437        Error = 1,
1438        ActiveCall = 2
1439    }
1440}
1441
1442wire_enum! {
1443    /// Button definitions use the stimulus values plus provisioning-only
1444    /// placeholder values in the 0xf1..=0xf5 range.
1445    pub enum ButtonType {
1446        /// An unassigned physical slot that should not present an actionable station key.
1447        Unused = 0x00,
1448        /// A key that redials the most recently dialed destination.
1449        LastNumberRedial = 0x01,
1450        /// A programmable key bound to a configured speed-dial destination.
1451        SpeedDial = 0x02,
1452        /// The station hold key used to hold or resume the current call.
1453        Hold = 0x03,
1454        /// The station transfer key used to start or complete a call transfer.
1455        Transfer = 0x04,
1456        /// A feature key for configuring or toggling call-forward-all.
1457        ForwardAll = 0x05,
1458        /// A feature key for configuring or toggling call-forward-on-busy.
1459        ForwardBusy = 0x06,
1460        /// A feature key for configuring or toggling call-forward-on-no-answer.
1461        ForwardNoAnswer = 0x07,
1462        /// A legacy display-oriented station key whose behavior is phone-model specific.
1463        Display = 0x08,
1464        /// A line-appearance key representing a directory number and its calls.
1465        Line = 0x09,
1466        /// A key launching the T.120 text-chat application.
1467        T120Chat = 0x0a,
1468        /// A key launching the T.120 shared-whiteboard application.
1469        T120Whiteboard = 0x0b,
1470        /// A key launching T.120 application sharing.
1471        T120ApplicationSharing = 0x0c,
1472        /// A key launching T.120 conference file transfer.
1473        T120FileTransfer = 0x0d,
1474        /// A station key assigned to video control.
1475        Video = 0x0e,
1476        /// A programmable voicemail-access key, commonly paired with message-waiting indication.
1477        Voicemail = 0x0f,
1478        /// A combined key that answers an offered call or releases the current call.
1479        AnswerRelease = 0x10,
1480        /// A feature key controlling automatic call answer.
1481        AutoAnswer = 0x11,
1482        /// A key that selects calls for transfer, conference, or other multi-call operations.
1483        Select = 0x12,
1484        /// A generic programmable feature key whose concrete action is supplied by provisioning.
1485        Feature = 0x13,
1486        /// A programmable key opening a provisioned phone-service URL.
1487        ServiceUrl = 0x14,
1488        /// A speed-dial key whose lamp also displays the target's busy-lamp-field state.
1489        BlfSpeedDial = 0x15,
1490        /// A feature key that parks a call at a specified destination.
1491        DirectedPark = 0x16,
1492        /// A key representing an intercom appearance or intercom destination.
1493        Intercom = 0x17,
1494        /// A feature key for malicious-call identification.
1495        MaliciousCall = 0x1b,
1496        /// Application-defined programmable key B1; SCCP assigns no universal action.
1497        GenericAppB1 = 0x21,
1498        /// Application-defined programmable key B2; SCCP assigns no universal action.
1499        GenericAppB2 = 0x22,
1500        /// Application-defined programmable key B3; SCCP assigns no universal action.
1501        GenericAppB3 = 0x23,
1502        /// Application-defined programmable key B4; SCCP assigns no universal action.
1503        GenericAppB4 = 0x24,
1504        /// Application-defined programmable key B5; SCCP assigns no universal action.
1505        GenericAppB5 = 0x25,
1506        /// A generic feature key whose lamp can display multiple blink states.
1507        MultiblinkFeature = 0x26,
1508        /// A feature key entering the dial-in Meet-Me conference workflow.
1509        MeetMeConference = 0x7b,
1510        /// The station conference key used to build or manage an ad-hoc conference.
1511        Conference = 0x7d,
1512        /// A feature key that parks the current call.
1513        CallPark = 0x7e,
1514        /// A feature key that answers a ringing call in the configured pickup scope.
1515        CallPickup = 0x7f,
1516        /// A feature key for group pickup outside the station's immediate pickup group.
1517        GroupCallPickup = 0x80,
1518        /// A feature key for extension-mobility login, logout, or appearance control.
1519        Mobility = 0x81,
1520        /// A feature key that exposes and changes do-not-disturb state.
1521        DoNotDisturb = 0x82,
1522        /// A feature key opening the conference participant list.
1523        ConferenceList = 0x83,
1524        /// A conference key that removes the most recently added participant.
1525        RemoveLastParticipant = 0x84,
1526        /// A feature key launching Cisco's call-quality reporting tool.
1527        QualityReportTool = 0x85,
1528        /// A feature key requesting callback when a busy or unavailable destination becomes reachable.
1529        Callback = 0x86,
1530        /// A feature key for Cisco's alternate call-pickup workflow.
1531        OtherPickup = 0x87,
1532        /// A feature key that changes the call's video mode.
1533        VideoMode = 0x88,
1534        /// A key that creates a new outbound call appearance.
1535        NewCall = 0x89,
1536        /// A key that releases the selected call.
1537        EndCall = 0x8a,
1538        /// A feature key that logs the station into or out of a hunt group.
1539        HuntGroupLogin = 0x8b,
1540        /// A feature key for Cisco call-queuing behavior.
1541        Queuing = 0x8f,
1542        /// A feature key opening a parking-lot view or retrieval workflow.
1543        ParkingLot = 0xc0,
1544        /// The station's fixed Messages key rather than a programmable voicemail slot.
1545        Messages = 0xc2,
1546        /// The station's fixed Directories key.
1547        Directory = 0xc3,
1548        /// The station's fixed Applications or Services key.
1549        Application = 0xc5,
1550        /// The station's fixed headset-control key.
1551        Headset = 0xc6,
1552        /// A template entry representing the physical dialing keypad rather than one programmable key.
1553        Keypad = 0xf0,
1554        /// Cisco template placeholder for a multi-purpose programmable position.
1555        /// Provisioning is expected to replace it with a concrete button definition.
1556        PlaceholderMulti = 0xf1,
1557        /// Cisco template placeholder reserved for a line appearance.
1558        /// Provisioning is expected to replace it with a concrete line definition.
1559        PlaceholderLine = 0xf2,
1560        /// Cisco template placeholder reserved for a speed dial.
1561        /// Provisioning is expected to replace it with a concrete speed-dial definition.
1562        PlaceholderSpeedDial = 0xf3,
1563        /// Cisco template placeholder reserved for a monitored hint or BLF target.
1564        /// Provisioning is expected to replace it with a concrete definition.
1565        PlaceholderHint = 0xf4,
1566        /// Cisco template placeholder reserved for abbreviated dialing.
1567        /// Provisioning is expected to replace it with a concrete definition.
1568        PlaceholderAbbreviatedDial = 0xf5,
1569        /// A station control for acoustic echo-cancellation behavior.
1570        AcousticEchoCancellation = 0xfd,
1571        /// A button definition Cisco marks as undefined; it is distinct from an intentionally unused slot.
1572        Undefined = 0xff
1573    }
1574}
1575
1576wire_enum! {
1577    /// SRTP encryption algorithm selected for a media channel.
1578    pub enum EncryptionMethod {
1579        None = 0,
1580        Aes128HmacSha1_32 = 1,
1581        Aes128HmacSha1_80 = 2,
1582        F8_128HmacSha1_32 = 3,
1583        F8_128HmacSha1_80 = 4,
1584        AeadAes128Gcm = 5,
1585        AeadAes256Gcm = 6
1586    }
1587}
1588
1589wire_enum! {
1590    /// SRTP algorithm support advertised by a media capability.
1591    pub enum EncryptionCapability {
1592        NotCapable = 0,
1593        Capable = 1
1594    }
1595}
1596
1597wire_enum! {
1598    /// History bucket assigned to a completed call.
1599    pub enum CallHistoryDisposition {
1600        Ignore = 0,
1601        Placed = 1,
1602        Received = 2,
1603        Missed = 3,
1604        ProtocolUnknown = 0xffff_fffe
1605    }
1606}
1607
1608wire_enum! {
1609    /// Station speaker state selected by call control.
1610    pub enum SpeakerMode {
1611        On = 1,
1612        Off = 2
1613    }
1614}
1615
1616wire_enum! {
1617    /// Station microphone state selected by call control.
1618    pub enum MicrophoneMode {
1619        On = 1,
1620        Off = 2
1621    }
1622}
1623
1624wire_enum! {
1625    /// Media resource allocated for a station-managed conference.
1626    pub enum ConferenceResourceType {
1627        Conference = 0,
1628        InteractiveVoiceResponse = 1
1629    }
1630}
1631
1632wire_enum! {
1633    /// Outcome returned by conference creation.
1634    pub enum CreateConferenceResult {
1635        Ok = 0,
1636        ResourceNotAvailable = 1,
1637        ConferenceAlreadyExists = 2,
1638        SystemError = 3
1639    }
1640}
1641
1642wire_enum! {
1643    /// Outcome returned by conference deletion.
1644    pub enum DeleteConferenceResult {
1645        Ok = 0,
1646        ConferenceDoesNotExist = 1,
1647        SystemError = 2
1648    }
1649}
1650
1651wire_enum! {
1652    /// Outcome returned by conference modification.
1653    pub enum ModifyConferenceResult {
1654        Ok = 0,
1655        ResourceNotAvailable = 1,
1656        ConferenceDoesNotExist = 2,
1657        InvalidParameter = 3,
1658        MoreActiveCallsThanReserved = 4,
1659        InvalidResourceType = 5,
1660        SystemError = 6
1661    }
1662}
1663
1664wire_enum! {
1665    /// Outcome returned when attaching a conference participant.
1666    pub enum AddParticipantResult {
1667        Ok = 0,
1668        ResourceNotAvailable = 1,
1669        ConferenceDoesNotExist = 2,
1670        DuplicateCallReference = 3,
1671        SystemError = 4
1672    }
1673}
1674
1675wire_enum! {
1676    /// Outcome returned by a conference-participant audit.
1677    pub enum AuditParticipantResult {
1678        Ok = 0,
1679        ConferenceDoesNotExist = 1
1680    }
1681}
1682
1683bitflags! {
1684    /// Identity fields a station must suppress for a conference participant.
1685    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
1686    pub struct PartyInformationRestrictions: u32 {
1687        const CALLING_NAME = 1 << 0;
1688        const CALLING_NUMBER = 1 << 1;
1689        const CALLED_NAME = 1 << 2;
1690        const CALLED_NUMBER = 1 << 3;
1691        const ORIGINAL_CALLED_NAME = 1 << 4;
1692        const ORIGINAL_CALLED_NUMBER = 1 << 5;
1693        const LAST_REDIRECT_NAME = 1 << 6;
1694        const LAST_REDIRECT_NUMBER = 1 << 7;
1695    }
1696}
1697
1698/// Negotiated inputs that select station-facing message layouts.
1699#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1700pub struct StationSessionContext {
1701    /// Negotiated frame and payload version.
1702    pub protocol: ProtocolVersion,
1703    /// Station feature bits that can select layouts independently of version.
1704    pub features: PhoneFeatures,
1705}
1706
1707impl StationSessionContext {
1708    /// Creates the layout-selection context for a registered station session.
1709    pub const fn new(protocol: ProtocolVersion, features: PhoneFeatures) -> Self {
1710        Self { protocol, features }
1711    }
1712
1713    /// Reports whether general UI responses use their dynamic string layouts.
1714    pub const fn uses_dynamic_general_ui(self) -> bool {
1715        self.protocol.uses_dynamic_general_ui()
1716            || self.features.contains(PhoneFeatures::DYNAMIC_MESSAGES)
1717    }
1718
1719    /// Reports whether feature status uses its dynamic response identifier.
1720    pub const fn uses_dynamic_feature_status(self) -> bool {
1721        self.features.contains(PhoneFeatures::DYNAMIC_MESSAGES)
1722    }
1723
1724    /// Reports whether speed-dial status uses the dynamic identifier and
1725    /// variable-string payload selected by the registered station session.
1726    pub const fn uses_dynamic_speed_dial_status(self) -> bool {
1727        self.protocol.uses_dynamic_speed_dial_status()
1728            || self.features.contains(PhoneFeatures::DYNAMIC_MESSAGES)
1729    }
1730
1731    /// Returns the call-info string layout selected by the negotiated version.
1732    pub const fn dynamic_call_info_layout(self) -> DynamicCallInfoLayout {
1733        self.protocol.dynamic_call_info_layout()
1734    }
1735
1736    /// Returns the dynamic service-URL string count selected by the session.
1737    pub const fn dynamic_service_url_string_count(self) -> usize {
1738        if self.protocol.wire() >= ProtocolVersion::V19.wire() {
1739            3
1740        } else {
1741            2
1742        }
1743    }
1744}
1745
1746impl From<ProtocolVersion> for StationSessionContext {
1747    fn from(protocol: ProtocolVersion) -> Self {
1748        Self::new(protocol, PhoneFeatures::empty())
1749    }
1750}
1751
1752/// Dynamic RTP payload type used for telephone-event DTMF when a station is
1753/// configured to send digits through the media stream.
1754pub const RFC2833_TELEPHONE_EVENT_PAYLOAD: u8 = 101;
1755
1756impl DtmfMode {
1757    /// Resolves the automatic policy against the feature bits advertised by
1758    /// the registered station. Explicit policies are always preserved.
1759    pub const fn resolve(self, features: PhoneFeatures) -> Self {
1760        match self {
1761            Self::Auto if features.contains(PhoneFeatures::RFC2833) => Self::Rfc2833,
1762            Self::Auto => Self::Skinny,
1763            explicit => explicit,
1764        }
1765    }
1766
1767    /// Returns the wire payload type for the resolved policy. A zero payload
1768    /// tells the station to report connected-call digits as signaling events.
1769    pub const fn telephone_event_payload(self, features: PhoneFeatures) -> u8 {
1770        match self.resolve(features) {
1771            Self::Rfc2833 => RFC2833_TELEPHONE_EVENT_PAYLOAD,
1772            Self::Skinny | Self::Unknown(_) => 0,
1773            Self::Auto => unreachable!(),
1774        }
1775    }
1776}
1777
1778bitflags! {
1779    /// Feature flags advertised in the three-byte station protocol field.
1780    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
1781    pub struct PhoneFeatures: u32 {
1782        // The low byte contains the protocol version. Feature bits occupy the
1783        // following three bytes.
1784        const PORT_REQUEST = 1 << 17;
1785        const UTF8 = 1 << 20;
1786        const DYNAMIC_MESSAGES = 1 << 24;
1787        const RFC2833 = 1 << 26;
1788        const INTERNAL_CM_MEDIA = 1 << 28;
1789        const MULTIPLE_ACTIVE_CALLS = 1 << 30;
1790        const ABBREVIATED_DIAL = 1 << 31;
1791    }
1792}
1793
1794bitflags! {
1795    /// Permitted media directions in a capability entry.
1796    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
1797    pub struct ReceiveTransmit: u32 {
1798        const RECEIVE = 1;
1799        const TRANSMIT = 2;
1800    }
1801}
1802
1803#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1804/// One keypad digit, including the extended A-D symbols.
1805pub enum Digit {
1806    /// A numeric digit; valid decoded values are zero through nine.
1807    Number(u8),
1808    Star,
1809    Pound,
1810    A,
1811    B,
1812    C,
1813    D,
1814    /// An unrecognized keypad word retained from the wire.
1815    Unknown(u32),
1816}
1817
1818impl Digit {
1819    /// Converts a keypad wire word to a typed digit without discarding unknowns.
1820    pub const fn from_keypad(value: u32) -> Self {
1821        match value {
1822            0..=9 => Self::Number(value as u8),
1823            10 => Self::Star,
1824            11 => Self::Pound,
1825            12 => Self::A,
1826            13 => Self::B,
1827            14 => Self::C,
1828            15 => Self::D,
1829            value => Self::Unknown(value),
1830        }
1831    }
1832
1833    /// Returns the numeric keypad word used on the wire.
1834    pub const fn keypad_value(self) -> u32 {
1835        match self {
1836            Self::Number(number) => number as u32,
1837            Self::Star => 10,
1838            Self::Pound => 11,
1839            Self::A => 12,
1840            Self::B => 13,
1841            Self::C => 14,
1842            Self::D => 15,
1843            Self::Unknown(value) => value,
1844        }
1845    }
1846
1847    /// Returns the printable digit, or `?` for an invalid/unknown value.
1848    pub fn as_char(self) -> char {
1849        match self {
1850            Self::Number(n) if n <= 9 => char::from(b'0' + n),
1851            Self::Number(_) => '?',
1852            Self::Star => '*',
1853            Self::Pound => '#',
1854            Self::A => 'A',
1855            Self::B => 'B',
1856            Self::C => 'C',
1857            Self::D => 'D',
1858            Self::Unknown(_) => '?',
1859        }
1860    }
1861}
1862
1863impl From<u32> for Digit {
1864    fn from(value: u32) -> Self {
1865        Self::from_keypad(value)
1866    }
1867}
1868
1869impl From<Digit> for u32 {
1870    fn from(value: Digit) -> Self {
1871        value.keypad_value()
1872    }
1873}
1874
1875#[cfg(test)]
1876mod tests {
1877    use super::*;
1878
1879    #[test]
1880    fn protocol_versions_select_layout_profiles() {
1881        assert_eq!(ProtocolVersion::new(3).unwrap().layout(), LayoutProfile::V3);
1882        assert_eq!(
1883            ProtocolVersion::new(14).unwrap().layout(),
1884            LayoutProfile::V11
1885        );
1886        assert_eq!(ProtocolVersion::new(13).unwrap(), ProtocolVersion::V13);
1887        assert_eq!(ProtocolVersion::new(14).unwrap(), ProtocolVersion::V14);
1888        assert_eq!(
1889            ProtocolVersion::new(18).unwrap().layout(),
1890            LayoutProfile::V18
1891        );
1892        assert_eq!(
1893            ProtocolVersion::new(21).unwrap().layout(),
1894            LayoutProfile::V19
1895        );
1896        assert_eq!(
1897            ProtocolVersion::new(22).unwrap().layout(),
1898            LayoutProfile::V22
1899        );
1900        assert_eq!(
1901            ProtocolVersion::negotiate(99).unwrap(),
1902            ProtocolVersion::V22
1903        );
1904        assert!(ProtocolVersion::new(2).is_err());
1905    }
1906
1907    #[test]
1908    fn dynamic_station_layout_boundaries_follow_session_negotiation() {
1909        assert!(!ProtocolVersion::V8.uses_dynamic_general_ui());
1910        assert!(ProtocolVersion::V9.uses_dynamic_general_ui());
1911        assert!(!ProtocolVersion::V8.uses_dynamic_speed_dial_status());
1912        assert!(ProtocolVersion::V9.uses_dynamic_speed_dial_status());
1913
1914        assert_eq!(
1915            ProtocolVersion::V15.dynamic_call_info_layout(),
1916            DynamicCallInfoLayout::Fields12
1917        );
1918        assert_eq!(
1919            ProtocolVersion::V16.dynamic_call_info_layout(),
1920            DynamicCallInfoLayout::Fields13
1921        );
1922        assert_eq!(
1923            ProtocolVersion::V18.dynamic_call_info_layout(),
1924            DynamicCallInfoLayout::Fields13
1925        );
1926        assert_eq!(
1927            ProtocolVersion::V19.dynamic_call_info_layout(),
1928            DynamicCallInfoLayout::Fields15
1929        );
1930
1931        let baseline = StationSessionContext::from(ProtocolVersion::V8);
1932        assert!(!baseline.uses_dynamic_general_ui());
1933        assert!(!baseline.uses_dynamic_feature_status());
1934        let negotiated =
1935            StationSessionContext::new(ProtocolVersion::V8, PhoneFeatures::DYNAMIC_MESSAGES);
1936        assert!(negotiated.uses_dynamic_general_ui());
1937        assert!(negotiated.uses_dynamic_feature_status());
1938        assert!(negotiated.uses_dynamic_speed_dial_status());
1939    }
1940
1941    #[test]
1942    fn extensible_values_preserve_unknown_numbers() {
1943        let codec = Codec::from(0xfeed);
1944        assert_eq!(codec, Codec::Unknown(0xfeed));
1945        assert_eq!(codec.wire_value(), 0xfeed);
1946        let state = CallState::from(1000);
1947        assert_eq!(state.wire_value(), 1000);
1948    }
1949
1950    #[test]
1951    fn soft_key_events_are_template_positions() {
1952        assert_eq!(SoftKey::from(1), SoftKey::Redial);
1953        assert_eq!(SoftKey::from(13), SoftKey::Conference);
1954        assert_eq!(SoftKey::from(32), SoftKey::Dial);
1955        assert_eq!(SoftKey::from(201), SoftKey::Unknown(201));
1956    }
1957
1958    #[test]
1959    fn automatic_dtmf_uses_only_an_advertised_rfc2833_capability() {
1960        assert_eq!(
1961            DtmfMode::Auto.resolve(PhoneFeatures::RFC2833),
1962            DtmfMode::Rfc2833
1963        );
1964        assert_eq!(
1965            DtmfMode::Auto.telephone_event_payload(PhoneFeatures::RFC2833),
1966            RFC2833_TELEPHONE_EVENT_PAYLOAD
1967        );
1968        assert_eq!(
1969            DtmfMode::Auto.resolve(PhoneFeatures::empty()),
1970            DtmfMode::Skinny
1971        );
1972        assert_eq!(
1973            DtmfMode::Auto.telephone_event_payload(PhoneFeatures::empty()),
1974            0
1975        );
1976        assert_eq!(
1977            DtmfMode::Skinny.telephone_event_payload(PhoneFeatures::RFC2833),
1978            0
1979        );
1980        assert_eq!(
1981            DtmfMode::Rfc2833.telephone_event_payload(PhoneFeatures::empty()),
1982            RFC2833_TELEPHONE_EVENT_PAYLOAD
1983        );
1984    }
1985
1986    #[test]
1987    fn phone_feature_bits_match_the_three_register_feature_bytes() {
1988        // CP-7961G firmware SCCP41.9-4-2SR3-1S advertises protocol/features
1989        // 16 00 72 85. The final feature byte carries dynamic messages,
1990        // RFC2833, and abbreviated dial.
1991        let advertised = PhoneFeatures::from_bits_retain(0x8572_0000);
1992        assert!(advertised.contains(PhoneFeatures::PORT_REQUEST));
1993        assert!(advertised.contains(PhoneFeatures::UTF8));
1994        assert!(advertised.contains(PhoneFeatures::DYNAMIC_MESSAGES));
1995        assert!(advertised.contains(PhoneFeatures::RFC2833));
1996        assert!(advertised.contains(PhoneFeatures::ABBREVIATED_DIAL));
1997        assert!(!advertised.contains(PhoneFeatures::INTERNAL_CM_MEDIA));
1998        assert!(!advertised.contains(PhoneFeatures::MULTIPLE_ACTIVE_CALLS));
1999        assert!(
2000            PhoneFeatures::from_bits_retain(1 << 30).contains(PhoneFeatures::MULTIPLE_ACTIVE_CALLS)
2001        );
2002    }
2003
2004    #[test]
2005    fn every_named_wire_enum_value_is_unique_and_round_trips() {
2006        macro_rules! assert_wire_enum {
2007            ($type:ty) => {{
2008                let mut values = std::collections::HashSet::new();
2009                for value in <$type>::ALL_KNOWN {
2010                    assert!(
2011                        values.insert(value.wire_value()),
2012                        "duplicate {} value {value:?}",
2013                        stringify!($type)
2014                    );
2015                    assert_eq!(<$type>::from(value.wire_value()), *value);
2016                    assert!(value.is_known());
2017                }
2018            }};
2019        }
2020
2021        assert_wire_enum!(DeviceType);
2022        assert_wire_enum!(Codec);
2023        assert_wire_enum!(CallState);
2024        assert_wire_enum!(CallType);
2025        assert_wire_enum!(AlarmSeverity);
2026        assert_wire_enum!(MediaStatus);
2027        assert_wire_enum!(Stimulus);
2028        assert_wire_enum!(SoftKey);
2029        assert_wire_enum!(KeyMode);
2030        assert_wire_enum!(RingerMode);
2031        assert_wire_enum!(RingDuration);
2032        assert_wire_enum!(LampMode);
2033        assert_wire_enum!(Tone);
2034        assert_wire_enum!(ToneDirection);
2035        assert_wire_enum!(MediaPathId);
2036        assert_wire_enum!(MediaPathEvent);
2037        assert_wire_enum!(MediaPathCapability);
2038        assert_wire_enum!(MediaType);
2039        assert_wire_enum!(MediaTransport);
2040        assert_wire_enum!(QosDirection);
2041        assert_wire_enum!(QosReservationStyle);
2042        assert_wire_enum!(QosErrorCode);
2043        assert_wire_enum!(RsvpErrorCode);
2044        assert_wire_enum!(EndOfAnnouncementAck);
2045        assert_wire_enum!(AnnouncementPlayMode);
2046        assert_wire_enum!(AnnouncementPlayStatus);
2047        assert_wire_enum!(MessageWaitingResult);
2048        assert_wire_enum!(IpAddressType);
2049        assert_wire_enum!(ResetType);
2050        assert_wire_enum!(DtmfMode);
2051        assert_wire_enum!(CallForwardKind);
2052        assert_wire_enum!(CallPriority);
2053        assert_wire_enum!(NotificationPriority);
2054        assert_wire_enum!(CallInfoVisibility);
2055        assert_wire_enum!(CallSecurityState);
2056        assert_wire_enum!(BusyLampFieldState);
2057        assert_wire_enum!(SubscriptionCause);
2058        assert_wire_enum!(VideoFormat);
2059        assert_wire_enum!(MiscCommandType);
2060        assert_wire_enum!(EchoCancellation);
2061        assert_wire_enum!(SilenceSuppression);
2062        assert_wire_enum!(G723BitRate);
2063        assert_wire_enum!(UnregisterStatus);
2064        assert_wire_enum!(ButtonType);
2065        assert_wire_enum!(EncryptionMethod);
2066        assert_wire_enum!(EncryptionCapability);
2067        assert_wire_enum!(CallHistoryDisposition);
2068        assert_wire_enum!(SpeakerMode);
2069        assert_wire_enum!(MicrophoneMode);
2070        assert_wire_enum!(ConferenceResourceType);
2071        assert_wire_enum!(CreateConferenceResult);
2072        assert_wire_enum!(DeleteConferenceResult);
2073        assert_wire_enum!(ModifyConferenceResult);
2074        assert_wire_enum!(AddParticipantResult);
2075        assert_wire_enum!(AuditParticipantResult);
2076    }
2077
2078    #[test]
2079    fn codec_metadata_covers_static_and_cisco_dynamic_payloads() {
2080        assert_eq!(Codec::Pcmu.rtp_payload_type(), Some(0));
2081        assert_eq!(Codec::G711Ulaw56k.rtp_payload_type(), Some(0));
2082        assert_eq!(Codec::Pcma.rtp_payload_type(), Some(8));
2083        assert_eq!(Codec::G72248k.rtp_payload_type(), Some(9));
2084        assert_eq!(Codec::Wideband256k.rtp_payload_type(), Some(25));
2085        assert_eq!(Codec::Ilbc.rtp_payload_type(), Some(97));
2086        assert_eq!(Codec::G7221_32k.rtp_payload_type(), Some(102));
2087        assert_eq!(Codec::Opus.rtp_payload_type(), Some(107));
2088        assert_eq!(Codec::G726_32k.rtp_payload_type(), Some(112));
2089        assert_eq!(Codec::Wideband256k.sample_rate(), Some(16_000));
2090        assert_eq!(Codec::H264.kind(), CodecKind::Video);
2091        assert_eq!(Codec::ClearChannel.kind(), CodecKind::Data);
2092    }
2093}