classdef bleL2CAPFrameConfig < comm.internal.ConfigBase
%bleL2CAPFrameConfig Bluetooth LE L2CAP frame configuration parameters
%   CFGL2CAP = bleL2CAPFrameConfig creates a default Bluetooth(R) low
%   energy (LE) logical link control and adaptation protocol (L2CAP)
%   signaling command frame or data frame configuration object.
%
%   CFGL2CAP = bleL2CAPFrameConfig(Name, Value) creates a Bluetooth LE
%   L2CAP frame configuration object, CFGL2CAP, with the specified property
%   Name set to the specified Value. You can specify additional name-value
%   pair arguments in any order as (Name1, Value1, ..., NameN, ValueN).
%
%   bleL2CAPFrameConfig properties:
%
%   ChannelIdentifier             - Identifier for a logical channel
%                                   endpoint
%   CommandType                   - Signaling command type
%   SignalIdentifier              - Identifier for a request-response frame
%                                   exchange
%   CommandRejectReason           - Reason for rejecting the received
%                                   command frame
%   SourceChannelIdentifier       - Source logical channel endpoint
%   DestinationChannelIdentifier  - Destination logical channel endpoint
%   ConnectionIntervalRange       - Connection interval range
%   ConnectionTimeout             - Connection supervision timeout
%   PeripheralLatency             - Number of Link Layer connection events
%                                   a Peripheral can skip
%   ParameterUpdateResult         - Result of the connection parameters
%                                   update
%   LEPSM                         - LE (Low Energy) Protocol/Service
%                                   Multiplexer
%   MaxTransmissionUnit           - Maximum Service Data Unit (SDU) size
%   MaxPDUPayloadSize             - Maximum Protocol Data Unit (PDU)
%                                   payload size
%   Credits                       - Number of LE-frames that the peer
%                                   device can send/receive
%   ConnectionResult              - Result of the credit based connection
%                                   procedure
%
%   % Example 1:
%   % Create a bleL2CAPFrameConfig object for an L2CAP signaling frame
%   % of type 'Credit based connection request'.
%
%   cfgL2CAP = bleL2CAPFrameConfig;
%   cfgL2CAP.ChannelIdentifier  = '0005';
%   cfgL2CAP.CommandType        = 'Credit based connection request';
%   cfgL2CAP.Credits            = 10;
%   cfgL2CAP.SignalIdentifier   = '01'
%
%   % Example 2:
%   % Create a bleL2CAPFrameConfig object for an L2CAP data frame (B-frame)
%   % using Name-Value pairs.
%
%   cfgL2CAP = bleL2CAPFrameConfig('ChannelIdentifier', '0004')
%
%   % Example 3:
%   % Bluetooth LE L2CAP frames end-to-end workflow.
%
%   % Tx side
%   % Create a bleL2CAPFrameConfig object for an L2CAP data frame
%   % (B-frame).
%   cfgTx = bleL2CAPFrameConfig('ChannelIdentifier', '0004')
%
%   % Generate an L2CAP data frame (B-frame) with SDU from ATT as "0A0100".
%   l2capFrame = bleL2CAPFrame(cfgTx, "0A0100");
%
%   % Rx side
%   % Decode the received L2CAP frame.
%   [status, cfgRx, sdu] = bleL2CAPFrameDecode(l2capFrame);
%   if strcmp(status, 'Success') % Decoding is successful
%       cfgRx
%       sdu % SDU will be processed by the upper-layers (ex: ATT)
%   else
%       fprintf('L2CAP frame decoding failed due to ::');
%       status
%   end
%
%   See also bleL2CAPFrame, bleL2CAPFrameDecode.

%   Copyright 2018-2022 The MathWorks, Inc.

%#codegen
    
properties
    %ChannelIdentifier Identifier for a logical channel endpoint
    %   Specify the channel identifier as a 4-element character vector or
    %   string scalar denoting a 2-octet hexadecimal value. It is the local
    %   name representing a logical channel endpoint. It is used in
    %   identifying the command and data frames. Command frames use '0005'
    %   as the ChannelIdentifier. L2CAP B-frames use fixed channel
    %   identifiers, '0004' for ATT and '0006' for SMP. L2CAP LE-frames use
    %   other channel identifiers. The default value is '0005'. The default
    %   value is '0005' (for command frames).
    ChannelIdentifier = '0005'

    %CommandType Signaling command type
    %   Specify the command type as one of 'Command reject' |
    %   'Disconnection request' | 'Disconnection response' | 'Connection
    %   Parameter Update request' | 'Connection Parameter Update response'
    %   | 'Credit Based Connection request' | 'Credit Based Connection
    %   response' | 'Flow Control Credit'. It is the signaling command
    %   passed between two L2CAP entities. This property is applicable only
    %   when the channel identifier is set to '0005' (signaling channel
    %   identifier). The default value is 'Credit Based Connection
    %   request'.
    CommandType

    %SignalIdentifier Identifier for a request-response frame exchange
    %   Specify the signal identifier as a 2-element character vector or
    %   string scalar denoting a 1-octet hexadecimal value. The requesting
    %   device sets this field and the responding device uses the same
    %   value in its response. The value '00' is not accepted as the
    %   SignalIdentifier value in any command. The default value is '01'.
    SignalIdentifier = '01'

    %CommandRejectReason Reason for rejecting the received command frame
    %   Specify the reason for rejecting a command as one of 'Command not
    %   understood' | 'Signaling MTU exceeded' | 'Invalid CID in request'.
    %   It specifies why the signaling command frame was rejected. The
    %   default value is 'Command not understood'.
    CommandRejectReason

    %SourceChannelIdentifier Source logical channel endpoint
    %   Specify the source channel identifier as a 4-element character
    %   vector or string scalar denoting a 2-octet hexadecimal value. It
    %   specifies the source channel endpoint from which, sending the
    %   request or receiving the response. Once the channel has been
    %   created using credit based connection procedure, data packets
    %   flowing to the sender of the request shall be sent to this CID. The
    %   default value is '0040'.
    SourceChannelIdentifier = '0040'

    %DestinationChannelIdentifier Destination logical channel endpoint
    %   Specify the destination channel identifier as a 4-element character
    %   vector or string scalar denoting a 2-octet hexadecimal value. It
    %   specifies the destination channel endpoint from which, receiving
    %   the request or sending the response. Once the channel has been
    %   created using credit based connection procedure, data packets
    %   flowing to the sender of the response shall be sent to this CID.
    %   The default value is '0040'.
    DestinationChannelIdentifier = '0040'

    %ConnectionIntervalRange Connection interval range
    %   Specify the connection interval range as a 2-element numeric vector
    %   specified as [MIN, MAX]. MIN specifies the minimum value for the
    %   connection interval and MAX specifies the maximum value for the
    %   connection interval. MIN must be less than or equals to MAX. MIN
    %   and MAX are in the range [6, 3200]. Each unit for MIN or MAX is
    %   taken as 1.25 ms so that the resultant value is in the range [7.5
    %   ms, 4.0 s]. The default value is [6 3200].
    ConnectionIntervalRange = [6 3200]

    %ConnectionTimeout Connection supervision timeout
    %   Specify the connection timeout as an integer in the range [Mct,
    %   3200], where Mct is the larger of 10 and
    %   ((1+PeripheralLatency)*(ConnectionInterval*1.25)*2)/10. This
    %   property indicates the timeout for a connection if no valid packet
    %   is received within this time. Each unit is taken as 10 ms so that
    %   the resultant connection timeout (ConnectionTimeout*10) is in the
    %   range of 100 ms to 32.0 s. The default value is 10 (100 ms).
    ConnectionTimeout = 10

    %PeripheralLatency Number of Link Layer connection events a Peripheral
    %can skip
    %   Specify the Peripheral latency as an integer in the range [0, Msl],
    %   where Msl is the lesser of 499 and
    %   ((ConnectionTimeout*10)/((ConnectionInterval*1.25)*2))-1. This
    %   property indicates the number of connection events that a
    %   Peripheral might skip listening for packets from the Central. The
    %   default value is 0.
    PeripheralLatency = 0

    %ParameterUpdateResult Result of the connection parameters update
    %   Specify the result after updating the connection parameters as one
    %   of 'Accepted' | 'Rejected'. It indicates the response to the
    %   'Connection Parameter Update Request'. The default value is
    %   'Accepted'.
    ParameterUpdateResult

    %LEPSM LE Protocol/Service Multiplexer
    %   Specify the LEPSM as a 4-element character vector or string scalar
    %   denoting a 2-octet hexadecimal value. It is the unique number
    %   specified by the SIG (Special Interest Group) for each protocol.
    %   Values within the range [0x0001, 0x007F] are assigned by SIG for a
    %   set of existing protocols. Values within the range [0x0080, 0x00FF]
    %   are dynamically assigned for implemented protocols. The default
    %   value is '001F' for Attribute Protocol (ATT).
    LEPSM = '001F'

    %MaxTransmissionUnit Maximum Service Data Unit (SDU) size
    %   Specify the maximum transmission unit as an integer in the range of
    %   [23, 65535] octets. MaxTransmissionUnit specifies the maximum SDU
    %   size the upper-layer entity is capable of accepting. The default
    %   value is 23.
    MaxTransmissionUnit = 23

    %MaxPDUPayloadSize Maximum Protocol Data Unit (PDU) payload size
    %   Specify the maximum PDU payload size as an integer in the range of
    %   [23, 65533] octets. MaxPDUPayloadSize specifies the maximum
    %   payload data the L2CAP layer entity is capable of accepting. The
    %   default value is 23.
    MaxPDUPayloadSize = 23

    %Credits Number of LE-frames that the peer device can send/receive
    %   Specify the credits as an integer in the range [0, 65535]. Its
    %   value indicates that the number of LE-frames that the peer device
    %   is capable of receiving/sending. For command type 'Flow Control
    %   Credit', value 0 is not allowed. The default value is 1.
    Credits = 1

    %ConnectionResult Result of the credit based connection procedure
    %   Specify the connection result as one of 'Successful' | 'LEPSM not
    %   supported' | 'No resources available' | 'Insufficient
    %   authentication' | 'Insufficient authorization' | 'Insufficient
    %   encryption key size' | 'Insufficient encryption' | 'Invalid Source
    %   CID' | 'Source CID already allocated' | 'Unacceptable parameters'.
    %   It indicates the outcome of the connection request. The default
    %   value is 'Successful'.
    ConnectionResult
end

properties(Hidden)
    %HideDisplay Hide the properties of L2CAP frame configuration object
    %   This is a hidden property used by <a href="matlab:help('bleL2CAPFrameDecode')">bleL2CAPFrameDecode</a> function to
    %   disable the display of all the properties when L2CAP frame decoding
    %   is failed.
    HideDisplay (1, 1) logical = false
end

properties(Constant, Hidden)
    CommandType_Values = {...
        'Command reject'; ...
        'Disconnection request'; ...
        'Disconnection response'; ...
        'Connection parameter update request'; ...
        'Connection parameter update response'; ...
        'Credit based connection request'; ...
        'Credit based connection response'; ...
        'Flow control credit'}

    CommandRejectReason_Values =  {...
        'Command not understood'; ...
        'Signaling MTU exceeded'; ...
        'Invalid CID in request'}

    ParameterUpdateResult_Values = {...
        'Accepted'; ...
        'Rejected'}

    ConnectionResult_Values = {...
        'Successful'; ...
        'LEPSM not supported'; ...
        'No resources available'; ...
        'Insufficient authentication'; ...
        'Insufficient authorization'; ...
        'Insufficient encryption key size'; ...
        'Insufficient encryption'; ...
        'Invalid source CID'; ...
        'Source CID already allocated'; ...
        'Unacceptable parameters'}

    ConnectionCommands = {...
        'Credit based connection request', ...
        'Credit based connection response'}

    DisconnectionCommands = {...
        'Disconnection request', ...
        'Disconnection response'}

    % List of valid channel identifiers used in L2CAP frames
    % (LE-frames/B-frames/Signaling frames)
    ChannelIdentifiers = cellstr(dec2hex([4 5 6 32 : 62 64 : 127], 4))

    % Fixed channel identifiers, '0004' (ATT) and '0006' (SMP) are used in
    % L2CAP B-frames. '0005' is used in command frames
    FixedCIDs = {'0004'; '0005'; '0006'}

    % Signaling channel identifier used in command frames
    SignalingCID = '0005'
    
    % Maximum size (MTU) of SDU (Service Data Unit) for L2CAP B-frames
    MaxBFrameSDU = 65535
    
    % Maximum size (MTU) of SDU for L2CAP LE-frames
    MaxLEFrameSDU = 65533
end

methods
    function obj = bleL2CAPFrameConfig(varargin)
        % For codegen initialize variable sized properties in constructor
        obj@comm.internal.ConfigBase('CommandType', ...
            'Credit based connection request', ...
            'CommandRejectReason', 'Command not understood', ...
            'ParameterUpdateResult', 'Accepted', ...
            'ConnectionResult', 'Successful', ...
            varargin{:});
    end

    function obj = validateChannelIdentifier(obj, value, propName)
        validatestring(value, obj.ChannelIdentifiers, ...
            mfilename, propName);
    end

    function obj = set.CommandType(obj, value)
        value = validatestring(value, obj.CommandType_Values, ...
            mfilename, 'CommandType');
        obj.CommandType = '';
        obj.CommandType = value;
    end

    function obj = set.ChannelIdentifier(obj, value)
        validateChannelIdentifier(obj, value, 'ChannelIdentifier');
        obj.ChannelIdentifier = char(value);
    end

    function obj = set.SignalIdentifier(obj, value)
        ble.internal.validateHex(value, 2, 'SignalIdentifier');
        identifier = hex2dec(value);
        validateattributes(identifier, {'numeric'}, {'scalar', 'integer', ...
            '>=', 1, '<=', 255}, mfilename, 'SignalIdentifier');
        obj.SignalIdentifier = char(value);
    end

    function obj = set.CommandRejectReason(obj, value)
        value = validatestring(value, ...
            obj.CommandRejectReason_Values, mfilename, 'CommandRejectReason');
        obj.CommandRejectReason = '';
        obj.CommandRejectReason = value;
    end

    function obj = set.SourceChannelIdentifier(obj, value)
        ble.internal.validateHex(value, 4, 'SourceChannelIdentifier');
        obj.SourceChannelIdentifier = char(value);
    end

    function obj = set.DestinationChannelIdentifier(obj, value)
        ble.internal.validateHex(value, 4, 'DestinationChannelIdentifier');
        obj.DestinationChannelIdentifier = char(value);
    end

    function obj = set.ConnectionIntervalRange(obj, value)
        validateattributes(value, {'numeric'}, {'vector', 'integer', ...
            'numel', 2, '>=', 6, '<=', 3200}, mfilename, ...
            'ConnectionIntervalRange');

        % Validate connection interval range
        coder.internal.errorIf((value(1) > value(2)), ...
            'bluetooth:bleL2CAP:InvalidL2CAPConnectionIntervalRange');
        obj.ConnectionIntervalRange = value;
    end

    function obj = set.ConnectionTimeout(obj, value)
        validateattributes(value, {'numeric'}, {'scalar', 'integer', ...
            '>=', 10, '<=', 3200}, mfilename, 'ConnectionTimeout');
        obj.ConnectionTimeout = value;
    end

    function obj = set.PeripheralLatency(obj, value)
        validateattributes(value, {'numeric'}, {'scalar', 'integer', ...
            'nonnegative', '<=', 499}, mfilename, 'PeripheralLatency');
        obj.PeripheralLatency = value;
    end

    function obj = set.ParameterUpdateResult(obj, value)
        value = validatestring(value, ...
            obj.ParameterUpdateResult_Values, mfilename, ...
            'ParameterUpdateResult');
        obj.ParameterUpdateResult = '';
        obj.ParameterUpdateResult = value;
    end

    function obj = set.LEPSM(obj, value)
        ble.internal.validateHex(value, 4, 'LEPSM');
        psm = hex2dec(value);
        validateattributes(psm, {'numeric'}, {'scalar', 'integer', ...
            '>=', 1, '<=', 255}, mfilename, 'LEPSM');
        obj.LEPSM = char(value);
    end

    function obj = set.MaxTransmissionUnit(obj, value)
        validateattributes(value, {'numeric'}, {'scalar', 'integer', ...
            '>=', 23, '<=', 65535}, mfilename, 'MaxTransmissionUnit');
        obj.MaxTransmissionUnit = value;
    end

    function obj = set.MaxPDUPayloadSize(obj, value)
        validateattributes(value, {'numeric'}, {'scalar', 'integer', ...
            '>=', 23, '<=', 65533}, mfilename, 'MaxPDUPayloadSize');
        obj.MaxPDUPayloadSize = value;
    end

    function obj = set.Credits(obj, value)
        validateattributes(value, {'numeric'}, {'scalar', 'integer', ...
            'nonnegative', '<=', 65535}, mfilename, 'Credits');
        obj.Credits = value;
    end

    function obj = set.ConnectionResult(obj, value)
        value = validatestring(value, ...
            obj.ConnectionResult_Values, mfilename, 'ConnectionResult');
        obj.ConnectionResult = '';
        obj.ConnectionResult = value;
    end
end

methods (Access = protected)
    function flag = isInactiveProperty(obj, prop)
        % Controls conditional display of properties
        flag = true;

        % Disable display of all the properties
        if obj.HideDisplay
            return;
        end

        if any(strcmp(prop, 'ChannelIdentifier'))
            % ChannelIdentifier is applicable for all frame types
            flag = false;

        elseif strcmpi(obj.ChannelIdentifier, obj.SignalingCID)
            switch prop
                case {'CommandType', 'SignalIdentifier'}
                    flag = false;

                case 'CommandRejectReason'
                    flag = ~strcmp(obj.CommandType, 'Command reject');

                case 'SourceChannelIdentifier'
                    if any(strcmp(obj.CommandType, [obj.DisconnectionCommands, ...
                            obj.ConnectionCommands{1}, 'Flow control credit']))
                        % SourceChannelIdentifier is applicable for 'Flow
                        % control credit', 'Credit based connection request',
                        % 'Credit based connection response', 'Disconnection
                        % request', 'Disconnection response'
                        flag = false;
                        
                    elseif strcmp(obj.CommandType, 'Command reject') && ...
                            strcmp(obj.CommandRejectReason, 'Invalid CID in request')
                        % SourceChannelIdentifier is applicable for 'Command
                        % reject' with reason 'Invalid CID in request'
                        flag = false;
                    end

                case 'DestinationChannelIdentifier'
                    if any(strcmp(obj.CommandType, [obj.DisconnectionCommands, ...
                            obj.ConnectionCommands{2}]))
                        % DestinationChannelIdentifier is applicable for 'Flow
                        % control credit', 'Credit based connection request',
                        % 'Credit based connection response', 'Disconnection
                        % request', 'Disconnection response'
                        flag = false;
                        
                    elseif strcmp(obj.CommandType, 'Command reject') && ...
                            strcmp(obj.CommandRejectReason, 'Invalid CID in request')
                        % DestinationChannelIdentifier is applicable for
                        % 'Command reject' with reason 'Invalid CID in request'
                        flag = false;
                    end

                case {'ConnectionIntervalRange', 'ConnectionTimeout', ...
                        'PeripheralLatency'}
                    % ConnectionIntervalRange, ConnectionTimeout and
                    % PeripheralLatency are applicable for 'Connection
                    % parameter update request'
                    flag = ~strcmp(obj.CommandType, ...
                        'Connection parameter update request');

                case 'ParameterUpdateResult'
                    % ParameterUpdateResult is applicable for 'Connection
                    % parameter update response'
                    flag = ~strcmp(obj.CommandType, ...
                        'Connection parameter update response');

                case 'LEPSM'
                    flag = ~strcmp(obj.CommandType, obj.ConnectionCommands{1});

                case 'MaxTransmissionUnit'
                    % MaxTransmissionUnit is applicable for 'Credit based
                    % connection request', 'Credit based connection response'
                    if any(strcmp(obj.CommandType, obj.ConnectionCommands))
                        flag = false;
                        
                    elseif strcmp(obj.CommandType, 'Command reject') && ...
                            strcmp(obj.CommandRejectReason, 'Signaling MTU exceeded')
                        % MaxTransmissionUnit is applicable for 'Command
                        % reject' with reason 'Invalid CID in request'
                        flag = false;
                    end

                case 'MaxPDUPayloadSize'
                    % MaxTransmissionUnit is applicable for 'Credit based
                    % connection request', 'Credit based connection
                    % response'
                    flag = ~any(strcmp(obj.CommandType, obj.ConnectionCommands));

                case 'Credits'
                    % Credits is applicable for 'Credit based connection
                    % request', 'Credit based connection response' and
                    % 'Flow control credit'
                    flag = ~any(strcmp(obj.CommandType, [obj.ConnectionCommands, ...
                        'Flow control credit']));

                case 'ConnectionResult'
                    % ConnectionResult is applicable for 'Credit based
                    % connection response'
                    flag = ~strcmp(obj.CommandType, ...
                        'Credit based connection response');
            end
        end
    end
end
end
