.. _sec-phy:

Physical Layer (PHY)
--------------------

Introduction
^^^^^^^^^^^^

The physical layer (:term:`PHY`) is the lowest layer of the Bluetooth low
energy protocol stack. It configures the physical parameters of the radio
transmission and reception. It determines how a bit (and its value) are
represented over the air. By switching the PHY, the physical properties of the
RF signal is changed.

The supported PHYs include LE 1M, 2M, and Coded PHY. All the projects in |STACK|
have support for all of these PHYs; APIs need to be called in the application
to utilize the feature.

The following HCI commands were added to support this feature:

  - LE Set PHY Command (:ble_api:`HCI_LE_SetPhyCmd`)
  - LE Set Default PHY Command (:ble_api:`HCI_LE_SetDefaultPhyCmd`)
  - LE Read PHY Command (:ble_api:`HCI_LE_ReadPhyCmd`)

When the :ble_api:`HCI_LE_SetPhyCmd` is called, the controller starts the PHY
Update Procedure to change the PHYs. The procedure consists of exchanging the
PHY preferences of both devices and negotiating the correct PHY to use based on
the PHY preferences. Depending on peer device capability and preference(s), the
PHY Update Procedure may not result in a change to the active PHY configuration.

.. _sec-phy_2mbps:

LE 2M PHY
^^^^^^^^^

The |STACK| supports transferring data over the mandatory symbol rate of 1
megasymbol per second (Msym/s) where 1 symbol represents 1 bit.
This results in a bit rate of 1 megabit per second (Mb/s), which is referred to
as LE 1M PHY.

The stack also supports an optional symbol rate of 2 Msym/s, with a
bit rate of 2 Mb/s, which is referred to as LE 2M PHY.

.. _sec-phy_coded:

LE Coded PHY
^^^^^^^^^^^^

LE Coded PHY allows the ability to have the signal range increase up to nearly
quadruple the range of LE 1M PHY. This is achieved by coding the signal, so the
Tx power stays the same. This means that the power consumption per time stays
the same. On the other hand, this coding entails a lower data throughput.

.. ifconfig:: device == 'cc23xx'

.. _phy_coded-s2-s8:

LE Coded S2 and S8
""""""""""""""""""

The LE Coded :term:`PHY` can be operated with two data rates:

-   **S2** : In LE Coded S=2 mode, each bit is represented by two symbols. Thus,
    the data rate is 500kbps. In this mode the range is roughly doubled compared
    to the LE 1M PHY.
-   **S8**: In LE Coded S=8, each bit is represented by eight symbols. This
    gives a data rate of 125kbps. In this mode the range is roughly quadrupled
    compared to the LE 1M PHY.

Every packet sent on LE Coded PHY contains a coding indicator (:term:`CI`),
which indicates the coding of the packet. Thus, when a packet is being received
on the LE Coded PHY, the receiver uses the coding indicator to determine the
coding of the packet. The full packet format of packets sent on LE Coded PHY is
found in the |CORESPEC|, Vol 6, Part B, Chapter 2.2 PACKET FORMAT FOR THE
LE CODED PHY.

Link Budget
"""""""""""

The LE Coded :term:`PHY` gives a higher sensitivity than the LE 1M PHY. The
receiver sensitivity is defined by how weak signals the receiver can receive
compared to the level of interfering RF power received simultaneously. If no
interference is present, the sensitivity level is determined by the thermal
background noise and will correspond to the sensitivity specified in the
datasheet.

In an operating RF link, the transmitter will transmit at a specified RF power
level, and a (usually very tiny) fraction of that RF power will be picked up by
the receiving antenna and fed to the receiver.  If that fraction is too small,
the received power level will drop below the receiver sensitivity level and the
link will fail.

The link budget is the ratio between transmitted power and the Rx sensitivity
level. For convenience, the link budget (LB) is usually denoted in a
logarithmic scale (dB). Output power and sensitivity are usually denoted in a
logarithmic scale relative to 1mW (dBm). This means that:

.. math::

  \textrm{LB}_{\textrm{dBm}} = \textrm{Tx Power} (\textrm{dBm}) - \textrm{Rx Sensitivity Level} (\textrm{dBm})

There are two ways to improve the link budget:

  1) Increase the output power
  2) Improve (reduce) the receiver sensitivity level

Increasing the output power is fairly straight forward, but it comes at the cost
of (sometimes significantly) increased power consumption. In addition, all
regulatory jurisdictions have limits on RF emission levels and unwanted spurious
emissions, both of which will increase if the transmit power is increased.

The other option, improving receiver sensitivity, is the path chosen by the
Bluetooth SIG when Bluetooth 5 was adopted with the intention of offering four
times the RF range. This was also chosen to provide the longest range Bluetooth
Low Energy solutions at lowest power consumption. In free space, a doubling of
the range requires a quadrupling of the link budget (or 6 dB increase). This
means that four times the range requires 12 dB better sensitivity for the LE
Coded PHY, when compared with the LE 1M PHY. The reference for LE 1M PHY is set
to -93 dBm. (Note that -93 dBm was chosen as a reference point for a standard
radio at the time the spec was written.) Since the reference for LE 1M PHY is 
-93 dBm, the LE Coded PHY needs a sensitivity of -105 dBm in order to achieve 
four times the range. For the true RX sensitivity of the |device| for the
different PHYs, please refer to the |device| datasheet.

See :numref:`LBfig` for an illustration of this.

.. _LBfig:
.. figure:: resources/LBfigure.*

      Link Budget Illustration

The link budget increase is achieved through a two-way approach:

-   The biggest improvement comes simply from the fact that the data rate is
    reduced to 1/8th, which means that every bit carries eight times more energy
    for any given power level. Theoretically, this allows the receiver to
    receive signals at 9 dB lower power levels and still accumulate the same
    energy per bit as before.
-   The last 3 dB can be achieved due to the coding employed. The -93 dBm
    comparison level (for 1Mpbs) assumed a standard differential demodulator,
    where each received symbol (1 symbol per bit) is determined to be "1" or
    "0" based on a comparison with the previous symbol. The Coded PHYs
    facilitate a semi-coherent receiver where eight symbols make up 1 bit, and
    correlators can search for these known symbol sequences. This is called
    Forward Error Correction (:term:`FEC`).

It is worth noting that even though the Tx power is the same for LE Coded PHY
as for LE 1M PHY, the data rate is lower, thus the device will spend a longer
time to send the same amount of data. This will give a higher power consumption
for sending the same amount of data on LE Coded PHY compared to LE 1M PHY.

You can read more about how the LE Coded PHY in this blog: `How does Bluetooth®
5 increase the achievable range of a Bluetooth Low Energy connection? <https://
e2e.ti.com/blogs_/b/connecting_wirelessly/archive/2017/01/30/how-does-bluetooth
-5-increase-the-achievable-range-of-a-bluetooth-low-energy-connection>`_

PHY Comparison
^^^^^^^^^^^^^^

A comparison of the Bluetooth Low Energy PHYs is given below:

.. table:: Comparison of the PHYs.

  +------------------+--------------+-------+--------------+--------------+
  | Parameter        | LE 1M        | LE 2M | LE Coded S=2 | LE Coded S=8 |
  +==================+==============+=======+==============+==============+
  | Symbol Rate      | 1Msps        | 2Msps | 1Msps        | 1Msps        |
  +------------------+--------------+-------+--------------+--------------+
  | Data Rate        | 1Mbps        | 2Mbps | 500kbps      | 125kbps      |
  +------------------+--------------+-------+--------------+--------------+
  | Error Correction | None         | None  | :term:`FEC`  | :term:`FEC`  |
  +------------------+--------------+-------+--------------+--------------+
  | Range Multiplier | 1            | ~0.8  | ~2           | ~4           |
  +------------------+--------------+-------+--------------+--------------+

LE 2M PHY vs LE 1M PHY
""""""""""""""""""""""

The LE 2M PHY feature uses the same transmit power as the LE 1M PHY, the only
change is in the modulation of data in the PHY. Using the LE 2M PHY, the energy
consumption decreases due to higher data modulation at the same output power.
The following table lists some of the differences between the two PHYs:

.. _PHY_tradeoffs:
.. table:: Tradeoff between 1M and 2M PHYs

   +------------------------------------------+----------------------------------------------------------------------------+
   |     Parameter                            |     Comparison                                                             |
   +==========================================+============================================================================+
   |     Power consumption                    |     Energy consumption is reduced using the same transmit power.           |
   +------------------------------------------+----------------------------------------------------------------------------+
   |     Data Rate                            |     LE 2M PHY is 2x faster to transmit data than LE 1M PHY.                |
   +------------------------------------------+----------------------------------------------------------------------------+
   |     Receive Sensitivity                  |     The link budget will be lower relative to LE 1M PHY, due to            |
   |                                          |     the increased symbol rate.                                             |
   +------------------------------------------+----------------------------------------------------------------------------+
   |     Transmit Power                       |     The output power is same for both PHYs.                                |
   +------------------------------------------+----------------------------------------------------------------------------+

The main advantage to use the LE 2M PHY is for high throughput applications to
transfer data at a higher speed.

LE Coded PHY vs LE 1M PHY
"""""""""""""""""""""""""

The LE Coded :term:`PHY` feature uses the same transmit power as the LE 1M PHY,
the only change is in the modulation of data in the PHY. Using the LE Coded
PHY, the energy consumption increases because the radio is in Tx longer. Thus
the main application for LE Coded PHY should be applications that need a long
range, but a low data rate. According to the Bluetooth spec, there are
limitations on what packets can be sent on the LE Coded PHY.

.. _advert-PHY:

Advertising PHY
^^^^^^^^^^^^^^^
.. ifconfig:: device != 'cc23xx'

  We will use a snippet similar to the example application **simple peripheral**
  to show how to advertise on multiple PHYs by selecting a PHY for each
  advertisement set. Please use :ref:`gap-advertiser` as a reference for the
  advertisement :term:`API` s.

  Set up the advertising parameters for each set and specify the PHY. The snippet
  provides a way to use a predefined parameter set
  (e.g. :ble_api:`GAPADV_PARAMS_LEGACY_SCANN_CONN`). The primary PHY and secondary
  PHY can also be individually selected (e.g. :ble_api:`GAP_ADV_PRIM_PHY_CODED_S2`
  or :ble_api:`GAP_ADV_SEC_PHY_CODED_S8`) in the advertisement parameters:

  .. code-block:: c
    :caption: Create two advertisement sets at different PHYs
    :linenos:
    :emphasize-lines: 6, 13

      // Advertising handles
      static uint8 advHandleLegacy;
      static uint8 advHandleLongRange;

      // Temporary memory for advertising parameters for set #1 (1M PHY)
      GapAdv_params_t advParamLegacy = GAPADV_PARAMS_LEGACY_SCANN_CONN;

      // Create Advertisement set #1 and assign handle
      status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLegacy,
                             &advHandleLegacy);

      // Use long range params to create long range set #2 (Coded PHY S2)
      GapAdv_params_t advParamLongRange = GAPADV_PARAMS_AE_LONG_RANGE_CONN;

      // Alternatively, set the parameters individually.
      advParamLongRange.primPhy = GAP_ADV_PRIM_PHY_CODED_S2;
      advParamLongRange.secPhy = GAP_ADV_SEC_PHY_CODED_S8;

      // Create Advertisement set #2 and assign handle
      status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLongRange,
                             &advHandleLongRange);

.. ifconfig:: device == 'cc23xx'

  For the **basic_ble** example project the advertising parameters, such as the
  advertising PHY, can be selected via SysConfig GUI under BLE - Broadcaster
  Configuration - Advertisement Set 1 - Advertisement Parameters 1. Currently
  the |SDK| only supports the selection of LE 1M PHY. The advertising
  parameters, generated by SysConfig, can be reviewed in ti_ble_config.c.

  .. code-block:: c
    :caption: Advertising parameters generated by SysConfig
    :linenos:
    :emphasize-lines: 10

    GapAdv_params_t advParams1 = {
      .eventProps =   GAP_ADV_PROP_CONNECTABLE | GAP_ADV_PROP_LEGACY | GAP_ADV_PROP_SCANNABLE,
      .primIntMin =   160,
      .primIntMax =   160,
      .primChanMap =  GAP_ADV_CHAN_ALL,
      .peerAddrType = PEER_ADDRTYPE_PUBLIC_OR_PUBLIC_ID,
      .peerAddr =     { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
      .filterPolicy = GAP_ADV_AL_POLICY_ANY_REQ,
      .txPower =      GAP_ADV_TX_POWER_NO_PREFERENCE,
      .primPhy =      GAP_ADV_PRIM_PHY_1_MBPS,
      .secPhy =       GAP_ADV_SEC_PHY_1_MBPS,
      .sid =          0
    };

According to the |CORESPEC| (Version 5.0 | Vol 6, Part B, Chapter 2.3
ADVERTISING CHANNEL PDU), only the following :term:`PDU` types can be
transmitted on the LE Coded :term:`PHY`:

- ADV_EXT_IND
- AUX_ADV_IND
- AUX_SYNC_IND
- AUX_CHAIN_IND
- AUX_SCAN_REQ
- AUX_SCAN_RSP
- AUX_CONNECT_REQ
- AUX_CONNECT_RSP

You can read about each PDU in the |CORESPEC|, Vol 6, Part B, Chapter 2.3
ADVERTISING CHANNEL PDU.

The primary PHY parameter will decide whether the device is advertising in
legacy mode, or in long range mode (with ADV_EXT_IND). The secondary PHY
parameter will decide the PHY of any auxiliary advertisement packets (i.e. all
packets beginning with AUX\_).

If advertising non-connectable and non-scannable, an ADV_EXT_IND PDU with no Adv
Data can be sent without an auxiliary packet. In all other cases, the
ADV_EXT_IND PDU must contain a pointer to an auxiliary advertisement packet,
AUX_ADV_IND. The AUX_ADV_IND PDU is sent on the PHY given in secPhy, and on one
of the secondary channels (also known as data channels).

.. _scan-PHY:

Scanning PHY
^^^^^^^^^^^^

.. ifconfig:: device != 'cc23xx'

  We will use a snippet similar to the example application **simple central** to
  show how the scanning PHY is selected. Please use :ref:`gapscanner` as a
  reference for the scanner :term:`API` s.
  
  Setup the scanner PHY parameters and the scan primary PHY
  (e.g. :ble_api:`SCAN_PRIM_PHY_1M`):
  
    .. code-block:: c
      :caption: Setup scan parameters
      :linenos:
      :emphasize-lines: 1
  
        GapScan_PrimPhy_t scanPhy = SCAN_PRIM_PHY_1M;
        GapScan_ScanType_t scanType = SCAN_TYPE_ACTIVE;
        uint16_t scanInt = SCAN_PARAM_DFLT_INTERVAL;
        uint16_t scanWindow = SCAN_PARAM_DFLT_INTERVAL;
  
        // Set Scan PHY parameters
        GapScan_setPhyParams(DEFAULT_SCAN_PHY, SCAN_TYPE_ACTIVE,
                           SCAN_PARAM_DFLT_INTERVAL, SCAN_PARAM_DFLT_INTERVAL);
  
        // Set scanning primary PHY
        GapScan_setParam(SCAN_PARAM_PRIM_PHYS, &scanPhy);
  
  To scan on multiple PHYs (e.g. :ble_api:`SCAN_PRIM_PHY_1M` and
  :ble_api:`SCAN_PRIM_PHY_CODED`), individual PHYs can be OR'ed together\:
  
    .. code-block:: c
      :caption: Multiple PHY scan
      :linenos:
      :emphasize-lines: 1
  
        GapScan_PrimPhy_t scanPhy = SCAN_PRIM_PHY_1M | SCAN_PRIM_PHY_CODED;
        GapScan_ScanType_t scanType = SCAN_TYPE_ACTIVE;
        uint16_t scanInt = SCAN_PARAM_DFLT_INTERVAL;
        uint16_t scanWindow = SCAN_PARAM_DFLT_INTERVAL;
  
        // Set Scan PHY parameters
        GapScan_setPhyParams(scanPhy, scanType,
                         scanInt, scanWindow);
  
        // Set scanning primary PHY
        GapScan_setParam(SCAN_PARAM_PRIM_PHYS, &scanPhy);

.. ifconfig:: device == 'cc23xx'

  For the **basic_ble** example project the scan parameters, such as the
  scanning PHY, are hardcoded in app_central.c with the constant declaration of
  **centralScanInitParams** or in app_observer.c with the constant declaration
  of **observerScanInitParams**.

  .. code-block:: c
    :caption: Scan parameters in app_central.c
    :linenos:
    :emphasize-lines: 4

    const BLEAppUtil_ScanInit_t centralScanInitParams =
    {
        /*! Opt SCAN_PRIM_PHY_1M | SCAN_PRIM_PHY_CODED */
        .primPhy                    = SCAN_PRIM_PHY_1M,
    
        /*! Opt SCAN_TYPE_ACTIVE | SCAN_TYPE_PASSIVE */
        .scanType                   = SCAN_TYPE_ACTIVE,
    
        /*! Scan interval shall be greater than or equal to scan window */
        .scanInterval               = 800, /* Units of 625 us */
    
        /*! Scan window shall be less than or equal to scan interval */
        .scanWindow                 = 800, /* Units of 625 us */
    
        /*! Select which fields of an advertising report will be stored */
        /*! in the AdvRptList, For mor field see @ref Gap_scanner.h     */
        .advReportFields            = SCAN_ADVRPT_FLD_ADDRESS |
                                      SCAN_ADVRPT_FLD_ADDRTYPE,
    
        /*! Opt SCAN_PRIM_PHY_1M | SCAN_PRIM_PHY_CODED */
        .scanPhys                   = SCAN_PRIM_PHY_1M,
    
        /*! Opt SCAN_FLT_POLICY_ALL | SCAN_FLT_POLICY_WL |   */
        /*! SCAN_FLT_POLICY_ALL_RPA | SCAN_FLT_POLICY_WL_RPA */
        .fltPolicy                  = SCAN_FLT_POLICY_ALL,
    
        /*! For more filter PDU @ref Gap_scanner.h */
        .fltPduType                 = SCAN_FLT_PDU_CONNECTABLE_ONLY |
                                      SCAN_FLT_PDU_COMPLETE_ONLY,
    
        /*! Opt SCAN_FLT_RSSI_ALL | SCAN_FLT_RSSI_NONE */
        .fltMinRssi                 = SCAN_FLT_RSSI_ALL,
    
        /*! Opt SCAN_FLT_DISC_NONE | SCAN_FLT_DISC_GENERAL | SCAN_FLT_DISC_LIMITED
         *  | SCAN_FLT_DISC_ALL | SCAN_FLT_DISC_DISABLE */
        .fltDiscMode                = SCAN_FLT_DISC_DISABLE,
    
        /*! Opt SCAN_FLT_DUP_ENABLE | SCAN_FLT_DUP_DISABLE | SCAN_FLT_DUP_RESET */
        .fltDup                     = SCAN_FLT_DUP_ENABLE,
    };

As noted in :ref:`phy_coded-s2-s8`, each packet transmitted on the LE Coded PHY
contains a coding indicator that tells the receiving device what the coding of
the packet is. It is thus not necessary to tell the :ref:`gapscanner` what
coding to use when scanning on the LE Coded PHY.

In order to send a scan request to an advertiser which is advertising on LE
Coded PHY, the scanner must listen for the AUX_ADV_IND indicated by the pointer
in the ADV_EXT_IND :term:`PDU`, and send a scan request to this packet. This
will be an AUX_SCAN_REQ, sent on the same PHY and channel as the AUX_ADV_IND. In
turn, the advertiser can send an AUX_SCAN_RSP on the same PHY and channel.

Please reference :ref:`limitations-PHY` if trying to use the LE 2M PHY when
scanning for extended advertisements.

.. _init-PHY:

Initiating PHY
^^^^^^^^^^^^^^

.. ifconfig:: device != 'cc23xx'

  We will use a snippet similar to the example application **simple central** to
  show how the initiating PHY is selected. Please use :ref:`gapinitiator` as a
  reference for the initiator :term:`API` s.
  
  Select which PHY to use when initiating the connection
  (e.g. :ble_api:`INIT_PHY_CODED`):
  
  .. code-block:: c
    :caption: Initiate a connection using LE Coded PHY
    :linenos:
    :emphasize-lines: 1
  
      GapInit_InitPhy_t initPhy = INIT_PHY_CODED;
  
      GapInit_connect(scanList[index].addrType & MASK_ADDRTYPE_ID,
                      scanList[index].addr, initPhy, 0);
  
  To initiate on multiple PHYs (e.g. :ble_api:`INIT_PHY_1M` and
  :ble_api:`INIT_PHY_CODED`), individual PHYs can be OR'ed together\:
  
  .. code-block:: c
    :caption: Multiple PHY init
    :linenos:
    :emphasize-lines: 1
  
      GapInit_InitPhy_t initPhy = INIT_PHY_1M | INIT_PHY_CODED;
  
      GapInit_connect(scanList[index].addrType & MASK_ADDRTYPE_ID,
                      scanList[index].addr, initPhy, 0);

.. ifconfig:: device == 'cc23xx'

  For the **basic_ble** example project the connection parameters, such as the
  initiating PHY, are hardcoded in app_central.c with the constant declaration
  of **centralConnParams**.

  .. code-block:: c
    :caption: Connection parameters in app_central.c
    :linenos:
    :emphasize-lines: 3

    BLEAppUtil_ConnectParams_t centralConnParams =
    {
        .phys = INIT_PHY_1M,
        .timeout = 0
    };

In order to send a connection request to an advertiser which is advertising on
LE Coded PHY, the initiator must listen for the AUX_ADV_IND indicated by the
pointer in the ADV_EXT_IND :term:`PDU`, and send a connection request to this
packet. This will be an AUX_CONNECT_REQ, sent on the same PHY and channel as the
AUX_ADV_IND. In turn, the advertiser can send an AUX_CONNECT_RSP on the same PHY
and channel.

.. _changing-PHY:

Changing PHY
^^^^^^^^^^^^

The application can initiate a :term:`PHY` Update Procedure in a connection
regardless of the role of the device (Central or Peripheral). The PHY preferences
that are set by :ble_api:`HCI_LE_SetDefaultPhyCmd` are used by default during a
set PHY negotiation.

.. attention::
    Calling :ble_api:`HCI_LE_SetPhyCmd` to change the PHY will change the
    preferred PHY of the device. This means that in some cases, only the device
    that first changed the active PHY can change it back.

    When the application has finished the PHY critical operations, it is
    therefore a good idea to change the PHY to a bit mask with every supported
    PHY, e.g. (LE 1M PHY | LE Coded PHY). This will allow the peer device to
    change the PHY back to LE 1M.

    The :ble_api:`HCI_LE_SetDefaultPhyCmd` is used to specify the preferred PHY
    for transmit and receive for all subsequent connections. However, when the
    :ble_api:`HCI_LE_SetPhyCmd` is used to change the PHY for the connection,
    the change only applies to that connection. Subsequent connections will
    revert to using the default PHYs.

    The :ble_api:`HCI_LE_SetDefaultPhyCmd` should be called before forming the
    connection and :ble_api:`HCI_LE_SetPhyCmd` can only be called during a
    connection. Also note that the :ble_api:`HCI_LE_SetDefaultPhyCmd` does not
    change the PHY, only the :ble_api:`HCI_LE_SetPhyCmd` can request a change
    to the PHY.

The parameters for :ble_api:`HCI_LE_SetDefaultPhyCmd` and
:ble_api:`HCI_LE_SetPhyCmd` are same. The allPhys parameter specifies whether
the other two parameters (txPhy and rxPhy) are used or not.
Central value of '1' indicates the client has no PHY preference for that
direction, while a '0' indicates that the corresponding parameter should be
used. The txPhy and rxPhy can be set to specify which PHY to use for
transmitting and receiving, respectively. Note that when all supported PHY are
specified, the stack always tries to select the fastest PHY during a set PHY
negotiation.

.. _phyOpts:
.. table:: HCI_LE_SetPhyCmd and HCI_LE_SetDefaultPhyCmd variables.

  +----------------------------------+-----------------+---------------+
  | Name                             | Usage           | Description   |
  +==================================+=================+===============+
  | :ble_api:`HCI_PHY_USE_PHY_PARAM` | allPhys         | Use Phy Param |
  +----------------------------------+-----------------+---------------+
  | :ble_api:`HCI_PHY_USE_ANY_PHY`   | allPhys         | Use any PHY   |
  +----------------------------------+-----------------+---------------+
  | :ble_api:`HCI_PHY_1_MBPS`        | txPhy and rxPhy | LE 1M PHY     |
  +----------------------------------+-----------------+---------------+
  | :ble_api:`HCI_PHY_2_MBPS`        | txPhy and rxPhy | LE 2M PHY     |
  +----------------------------------+-----------------+---------------+
  | :ble_api:`HCI_PHY_CODED`         | txPhy and rxPhy | LE Coded PHY  |
  +----------------------------------+-----------------+---------------+

In addition, for LE Coded PHY you can choose between S=2 and S=8 with the
phyOpts parameter. Use the following :term:`API` to change/set the PHY:

.. code-block:: c
  :caption: Call API to set the PHY

  // Set Phy Preference on the current connection. Apply the same value
  // for RX and TX.
  HCI_LE_SetPhyCmd(connectionHandle, HCI_PHY_USE_PHY_PARAM, HCI_PHY_CODED, HCI_PHY_CODED, 0);

Based on the PHY negotiation, the PHY will change if the peer remote device
supports the given PHY(s). Otherwise, it will continue using
the current PHY. After this command is sent, the controller
will send a :ble_api:`hciEvt_BLEPhyUpdateComplete_t` which will indicate
completion of this command:

.. ifconfig:: device != 'cc23xx'

  .. code-block:: c
    :caption: Receive PHY Update Complete event
    :linenos:
  
    static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg)
    {
      ...
      case HCI_LE_EVENT_CODE:
      {
        hciEvt_BLEPhyUpdateComplete_t *pPUC = (hciEvt_BLEPhyUpdateComplete_t*) pMsg;
  
        // A Phy Update Has Completed or Failed
        if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
        {
          if (pPUC->status != SUCCESS)
          {
            Display_printf(dispHandle, SBP_ROW_STATUS_1, 0, "PHY Change failure");
          }
          else
          {
            // Only symmetrical PHY is supported.
            // rxPhy should be equal to txPhy.
            Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
                           "PHY Updated to %s",
                           (pPUC->rxPhy == HCI_PHY_1_MBPS) ? "1M" :
                           (pPUC->rxPhy == HCI_PHY_2_MBPS) ? "2M" :
                            "CODED");
          }
          ...

.. ifconfig:: device == 'cc23xx'

  .. code-block:: c
    :caption: Receive PHY Update Complete event
    :linenos:

    void Connection_HciGAPEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
    {
      ...
        case BLEAPPUTIL_HCI_LE_EVENT_CODE:
        {
            hciEvt_BLEPhyUpdateComplete_t *pPUC = (hciEvt_BLEPhyUpdateComplete_t*) pMsgData;
    
            if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
            {
              if (pPUC->status != SUCCESS)
              {
                  MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Phy update failure - connHandle = %d",
                                    pPUC->connHandle);
              }
              else
              {
    #if !defined(Display_DISABLE_ALL)
                  char * currPhy =
                          (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1 Mbps" :
                          (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2 Mbps" :
                          (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value";
                  MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Phy update - connHandle = %d PHY = %s",
                                    pPUC->connHandle, currPhy);
    #endif // #if !defined(Display_DISABLE_ALL)
              }
          ...

In case this is not the first time a PHY change is attempted, and the controller
knows that the peer device does not support the desired PHY, a
:ble_api:`HCI_LE_SET_PHY` event will be received:

.. ifconfig:: device != 'cc23xx'

  .. code-block:: c
    :caption: Receive PHY Update Complete event
    :linenos:
  
    static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg)
    {
      ...
      case HCI_GAP_EVENT_EVENT:
      {
      ...
  
        // HCI Commands Events
        case HCI_COMMAND_STATUS_EVENT_CODE:
        {
          hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
          switch ( pMyMsg->cmdOpcode )
          {
            case HCI_LE_SET_PHY:
            {
            if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
            {
               Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
                              "PHY Change failure, peer does not support this");
            }
  
            ....

.. ifconfig:: device == 'cc23xx'

  .. code-block:: c
    :caption: Receive PHY Update Complete event
    :linenos:

    void Connection_HciGAPEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
    {
      ...
        case BLEAPPUTIL_HCI_COMMAND_STATUS_EVENT_CODE:
        {
            hciEvt_CommandStatus_t *pHciMsg = (hciEvt_CommandStatus_t *)pMsgData;
            switch ( event )
            {
              case HCI_LE_SET_PHY:
              {
                  if (pHciMsg->cmdStatus ==
                      HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
                  {
                      MenuModule_printf(APP_MENU_CONN_EVENT, 0,
                                        "Conn status: Phy update - failure, peer does not support this");
                  }
  
            ....

See :ref:`sec-hci` for more information on receiving HCI events.


The sequence diagram below shows the use case where the Central initiates the PHY
update procedure:

.. _fig-phy-update-master:
.. uml::
    :caption: Sequence diagram for changing PHY by Central
    :align: center

    @startuml
    hide footbox


		participant Central
		participant CentralLL
		participant PeripheralLL
		participant Peripheral


		group Establish connection
				Central -> CentralLL: GapInit_connect()
				note left
					Central (formerly known as 'Central') and Peripheral 
					(formerly known as 'Slave') are in connection
				end note
		end
		== LE 1M PHY ==
    ...

		group Change PHY
			Central -> CentralLL: HCI_LE_SetPhyCmd()
			note left
				Central application initiates PHY change
			end note
			...

			CentralLL -> PeripheralLL : LL_PHY_REQ
			PeripheralLL -> CentralLL : LL_PHY_RES
			note left
				Link layer messages exchanged
			end note
			...
			CentralLL -> PeripheralLL : LL_PHY_UPDATE_IND

		end
		== Change of PHY ==
    ...

		group Receive event
			CentralLL -> Central : LE PHY Update Complete
			note left
				Application receives event from stack
			end note
			PeripheralLL --> Peripheral : LE PHY Update Complete
		end

    @enduml


Alternatively, the Peripheral can also initiate the PHY Update Procedure as well
using the same API as shown below:

.. _fig-phy-update-slave:
.. uml::
    :caption: Sequence diagram for changing PHY by Peripheral
    :align: center

    @startuml
    hide footbox


    participant Central
		participant CentralLL
		participant PeripheralLL
    participant Peripheral

		group Establish connection
				Central -> CentralLL: GapInit_connect()
				note right
					Central (formerly known as 'Master') and Peripheral
					(formerly known as 'Slave')  are in connection
				end note
		end
		== LE 1M PHY ==
    ...

		group Change PHY
			PeripheralLL <- Peripheral: HCI_LE_SetPhyCmd()
			note left
				Peripheral application initiates PHY change
			end note
			...

			CentralLL <- PeripheralLL : LL_PHY_REQ
			note left
				Link layer messages exchanged
			end note
			...
			CentralLL -> PeripheralLL : LL_PHY_UPDATE_IND

		end
		== Change of PHY ==
    ...

		group Receive event
			CentralLL --> Central : LE PHY Update Complete
			PeripheralLL -> Peripheral : LE PHY Update Complete
			note left
				Application receives event from stack
			end note
		end

    @enduml


If the PHY does not change (for example, if the Central tries to change to a PHY not
supported by the Peripheral), then only the side that initiated the PHY Update
Procedure will get a :ble_api:`hciEvt_BLEPhyUpdateComplete_t` event. The other
side will not receive a :ble_api:`hciEvt_BLEPhyUpdateComplete_t` event if the
PHY is not changed. This is represented by dotted arrow lines in the flow charts above.

.. _negotiating-phy:

PHY Negotiation
"""""""""""""""

Determining when the :term:`PHY` will change can be determined by looking at the PHY
preferences of both devices after the :ble_api:`HCI_LE_SetPhyCmd` is called.
If both devices prefers to use LE 2M PHY, the PHY will change to LE 2M PHY. If the PHY is
changed to 2M due to Central preference of only 2M, the Peripheral cannot change
the PHY back to 1M until the Central changes its PHY preference to support 1M as
well. Similarly if the PHY is changed to 1M due to the Peripheral preference of only
1M, the Central will not be able to change the PHY to 2M until the Peripheral changes
its PHY preference to support 2M as well.

If one device initiates change to a PHY not supported on other remote device,
the initiating side will receive a :ble_api:`hciEvt_BLEPhyUpdateComplete_t`
event with nonzero status indicating the change was not successful. If the PHY
does not change after :ble_api:`HCI_LE_SetPhyCmd` is called, the connection
continues with the current PHY.

.. uml::
    :caption: Sequence diagram for failed changing PHY attempts.
    :align: center

    @startuml
    hide footbox

    participant Central
    participant CentralLL
    participant PeripheralLL
    participant Peripheral

    group Establish connection
        Central -> CentralLL: GapInit_connect()
				note right
						Central (formerly known as 'Master') and Peripheral 
						(formerly known as 'Slave') are in connection
				end note
    end
    == LE 1M PHY ==
    ...

    group Change PHY
      PeripheralLL <- Peripheral: HCI_LE_SetPhyCmd()
			note left
				Peripheral application initiates PHY change
			end note
      ...

      CentralLL <- PeripheralLL : LL_PHY_REQ
			note left
				Link layer messages exchanged
			end note
      ...
      CentralLL -> PeripheralLL : LL_UNKNOWN_RSP
      ...
      PeripheralLL -> Peripheral : LE PHY Update Complete: \n PHY Change Failure
			note left
				Application receives event from stack
			end note

    end
    == No Change of PHY ==
    ...
    group Consecutive PHY change attempts
      PeripheralLL <- Peripheral: HCI_LE_SetPhyCmd()
			note left
				Peripheral application initiates PHY change
			end note
    ...
    PeripheralLL -> Peripheral : LE set PHY status: \n Unsupported remote feature
			note left
				Application receives event from stack
			end note
    end

    @enduml

.. _disable-specific-phy-support-during-runtime:

Disable Specific PHY Support During Runtime
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In order to make sure that a device is not going to change PHY when requested from 
another device, the best approach is to mark this PHY as non-supported for the
other devices. This approach allows the supported PHYs list to be modified 
again or set back to its original support during runtime. It is also possible 
to have different PHY supports for different connections. The following code 
snippets showcase how to remove support for one or several PHYs during runtime.

.. ifconfig:: device == 'cc13xx_cc26xx'

  * Go to the initialization function of your application and add 
    ``HCI_LE_ReadLocalSupportedFeaturesCmd()`` to the very end of it. 
    This command is used to read the LE locally supported features of 
    the device and will trigger a ``hciEvt_CmdComplete_t`` status with 
    opcode: ``HCI_LE_READ_LOCAL_SUPPORTED_FEATURES``.
  * To update the PHY support based on this status, go to the function 
    that processes incoming HCI command complete events and add the following 
    case to the switch statement.
  * Use the ``LL_FEATURE_2M_PHY`` or ``LL_FEATURE_CODED_PHY`` flags to disable
    2M or Coded PHYs respectively.

.. ifconfig:: device == 'cc23xx'

  * Go to the ``Connection_start()`` function of your application and add 
    ``HCI_LE_ReadLocalSupportedFeaturesCmd()`` at the very end of it. 
    This command is used to read the LE locally supported features of the 
    device and will trigger a ``hciEvt_CmdComplete_t`` status with opcode: 
    ``HCI_LE_READ_LOCAL_SUPPORTED_FEATURES``.
  * To update the PHY support based on this status, go to the function that 
    processes incoming HCI command complete events and add the following case 
    to the switch statement.
  * Do not forget to add the ``BLEAPPUTIL_HCI_COMMAND_COMPLETE_EVENT_CODE`` 
    event to the handler ``eventMask`` at the ``connectionHciGAPHandler``.
  * Use the ``LL_FEATURE_2M_PHY`` or ``LL_FEATURE_CODED_PHY`` flags to disable
    2M or Coded PHYs respectively.

.. ifconfig:: device == 'cc13xx_cc26xx'

  .. code-block:: c
    :caption: simple_peripheral.c::SimplePeripheral_processCmdCompleteEvt() or project_zero:: ProjectZero_processCmdCompleteEvt() - Include a case to handle ``HCI_LE_READ_LOCAL_SUPPORTED_FEATURES`` events.
    :linenos:

    case HCI_LE_READ_LOCAL_SUPPORTED_FEATURES:
    {
        if (pktStatus == SUCCESS)
        {
          uint8_t featSet[8];

          // Get current feature set from received event (bits 1-9)
          // of the returned data
          memcpy( featSet, &pMsg->pReturnParam[1], 8 );

          // Clear relevant bit(s) of byte 1 
          CLR_FEATURE_FLAG( featSet[1], LL_FEATURE_2M_PHY );
          // CLR_FEATURE_FLAG( featSet[1], LL_FEATURE_CODED_PHY ); // This one is only if you want to disable LE Coded PHYs too

          // Update controller with modified features
          HCI_EXT_SetLocalSupportedFeaturesCmd( featSet );
      }
    break;
    }
    //...

.. ifconfig:: device == 'cc23xx'

  .. code-block:: c
    :caption: app_connection.c::connectionHciGAPHandler - Add ``BLEAPPUTIL_HCI_COMMAND_COMPLETE_EVENT_CODE`` event into ``.eventMask``.
    :linenos:
    :emphasize-lines: 7

    BLEAppUtil_EventHandler_t connectionHciGAPHandler =
    {
    .handlerType    = BLEAPPUTIL_HCI_GAP_TYPE,
    .pEventHandler  = Connection_HciGAPEventHandler,
    .eventMask      = BLEAPPUTIL_HCI_COMMAND_STATUS_EVENT_CODE |
                      BLEAPPUTIL_HCI_LE_EVENT_CODE |
                      BLEAPPUTIL_HCI_COMMAND_COMPLETE_EVENT_CODE
    };

.. ifconfig:: device == 'cc23xx'

  .. code-block:: c
    :caption: app_connection.c::Connection_HciGAPEventHandler() - Include a case to handle ``HCI_LE_READ_LOCAL_SUPPORTED_FEATURES`` events.
    :linenos:

    void Connection_HciGAPEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
    {
      case BLEAPPUTIL_HCI_COMMAND_COMPLETE_EVENT_CODE:
        {
            hciEvt_CmdComplete_t *pMsg = (hciEvt_CmdComplete_t *)pMsgData;
            uint8_t status = pMsg->pReturnParam[0];

            switch(pMsg->cmdOpcode)
            {
                case HCI_LE_READ_LOCAL_SUPPORTED_FEATURES:
                {
                    if (status == SUCCESS)
                    {
                      uint8_t featSet[8];

                      // Get current feature set from received event (bits 1-9)
                      // of the returned data
                      memcpy( featSet, &pMsg->pReturnParam[1], 8 );

                      // Clear relevant bit(s) of byte 1
                      CLR_FEATURE_FLAG( featSet[1], LL_FEATURE_2M_PHY );
                      // CLR_FEATURE_FLAG( featSet[1], LL_FEATURE_CODED_PHY ); // This one is only if you want to disable LE Coded PHYs too
                      
                      // Update controller with modified features
                      HCI_EXT_SetLocalSupportedFeaturesCmd( featSet );
                  }
                break;
                }
                default:
                {
                  break;
                }
            }
        }
        //...

.. _limitations-PHY:

PHY Limitations
^^^^^^^^^^^^^^^

The following are the current PHY limitations in |STACK|:

* The BLE controller does not support asymmetric connections where the
  connection uses different PHYs in each direction (RX and TX).

.. only:: sdk_targets_cc2640

   * For the CC2640R2, it is not possible to scan on LE 2M PHY as the secondary
     PHY. This is stated as a known issue in the release notes for the CC2640R2
     BLE5-Stack.
