This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Why is BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST coming from a GATT server?

Using nRF5 SDK 14.0.0 with Softdevice s132 v5.0.0 on nRF52832.

I've set up my nrf to be a GATT client (in central role), and I'm using bluez on my Linux desktop with a LairdTech BT831 dongle as a GATT server (in peripheral role).

I found that when the client connects to the server, the server sends an ATT EXCHANGE MTU REQUEST. This is received by the softdevice on the client and I get a BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST event, which is very confusing to me -- the MTU is in the client_rx_mtu field, which makes no sense because the server is telling me its MTU.

Q1: I thought the BLE spec was that only the client could initiate an ATT exchange MTU request?

Q2: Why wouldn't the softdevice event be something like BLE_GATTC_EVT_EXCHANGE_MTU_REQUEST? (I know, this isn't in the header files, but it's an event for the client, not the nonexistent server).

Thanks!

--Rob

Parents
  • Hey Rob,

    that is an excellent question. 

    From BLUETOOTH SPECIFICATION Version 5.0 | Vol 3, Part G:

    4.3 SERVER CONFIGURATION

     

    This procedure is used by the client to configure the Attribute Protocol. This procedure has only one sub-procedure used to set the MTU sizes.

     

    4.3.1 Exchange MTU

     

    This sub-procedure is used by the client to set the ATT_MTU to the maximum possible value that can be supported by both devices when the client supports a value greater than the default ATT_MTU for the Attribute Protocol. This subprocedure shall only be initiated once during a connection.

     

    This sub-procedure shall not be used on a BR/EDR physical link since the MTU size is negotiated using L2CAP channel configuration procedures.

     

    The Attribute Protocol Exchange MTU Request is used by this sub-procedure. The Client Rx MTU parameter shall be set to the maximum MTU that this client can receive.

     

    Two possible responses can be sent from the server for the Exchange MTU Request: Exchange MTU Response and Error Response.

     

    Error Response is returned if an error occurred on the server.

     

    The server shall respond to this message with an Exchange MTU Response with the Server Rx MTU parameter set to the maximum MTU that this server can receive.

     

    If the Error Response is sent by the server with the Error Code set to Request Not Supported, the Attribute Opcode is not supported and the default MTU shall be used.

     

    Once the messages have been exchanged, the ATT_MTU shall be set to the minimum of the Client Rx MTU and Server Rx MTU values. 

    "This sub-procedure is used by the client " Indicates that MTU exchange is only used by the Client, but the paragraph does not explicitly prohibit the server from doing the same. BT spec is usually not this vague in its description. 

    I need to start an investigation on our side. Do note that our GATT library will automatically issue an MTU exchange whenever a connection is established, regardless if it's a Client or Server. This does cause some issues with certain Android BLE stacks and BlueEZ. 

    EDIT:
    You can of course easily disable this GATT library feature.

    Cheers,

    Håkon.

  • This is a bug. If nrf_ble_gatt.c sends the MTU request from the GATT server, it causes problems. For example, HID over GATT on Windows 10 (at least the version on my Stream 7 tablet) will retrieve only the first packet of the report descriptor if the Windows device supports ATT over 23 but data length of only 27. As a result, HID over GATT will not work for any non-trivial device. One solution is to disable MTU over 23, but this severely limits the speed of HID over GATT. If, instead, the GATT server waits for the Windows device to send the request (*as it should*), it works correctly. Please provide a way to disable this incorrect use of the specification in the nrf_ble_gatt.c. It should only be sent by GATT clients. Thanks!

  • As a workaround you need to put in a check for the GATT role in nrf_ble_gatt.c in the function on_connected_evt, where it initiates the mtu exchange request with a call to sd_ble_gattc_exchange_mtu_request.  

  • FWIW this bit me when trying to implement a linux NUS client to upload logs from a device.  The workaround does allow for 247 MTU when the request comes from the linux client.   

    Unfortunately the workaround breaks the nRF UART v2.0 client app for android as that app doesn't request an MTU change.  Any suggestions for getting both to work?

  • You can delay the MTU exchange until after you know what kind of device you're connected to.

Reply Children
  • There is a further confusion I have when reading the text in the spec after the text quoted above.

    3.4.2.2

    ....

    If either Client Rx MTU or Service Rx MTU are incorrectly less than the default
    ATT_MTU, then the ATT_MTU shall not be changed and the ATT_MTU shall
    be the default ATT_MTU.
    If a device is both a client and a server, the following rules shall apply:
    1. A device's Exchange MTU Request shall contain the same MTU as the
    device's Exchange MTU Response (i.e. the MTU shall be symmetric).
    2. If MTU is exchanged in one direction, that is sufficient for both directions.
    3. It is permitted, (but not necessary - see 2.) to exchange MTU in both
    directions, but the MTUs shall be the same in each direction (see 1.)
    4. If an Attribute Protocol Request is received after the MTU Exchange
    Request is sent and before the MTU Exchange Response is received, the
    associated Attribute Protocol Response shall use the default MTU. Figure
    3.1 shows an example that is covered by this rule. In this case device A and
    device B both use the default MTU for the Attribute Protocol Response.
    5. Once the MTU Exchange Request has been sent, the initiating device shall
    not send an Attribute Protocol Indication or Notification until after the MTU
    Exchange Response has been received. Note: This stops the risk of a crossover
    condition where the MTU size is unknown for the Indication or
    Notification.

    If the device is playing the role of both server and client (or thinks it is) how do I interpret 5? As far as I know only servers send notification or indications. The bold text in 5 suggests that it is the server that sends the MTU request....contrary to all text above.

    HEELLLP!!! Confuse me more BT SIg!

  • Both Central and Peripheral devices can issue an MTU exchange request, regardless of GATT role. Our SoftDevice will handle the request so as to be in accordance with BT spec. 

    A handful of BT stacks do not expect a Peripheral to issue an MTU exchange first and can end up using undesirable MTU sizes for your link. The workaround is simply to delay the MTU exchange request a bit. 

    The SoftDevice API uses sd_ble_gattc_exchange_mtu_request, but it can be used by a GATT server as well. 



  • I am running into this same issue and looks like the thread is 2+ years old, still not marked as answered.

    I use nRF52, SDK 16.0.0 and I am working with a setup where all devices in my network are based on nRF52. One master establishes connections to multiple slaves.

    Looking at the implementation in nrf_ble_gatt.c (function on_connected_evt())  it is clear that the MTU exchange is requested for any new connection, regardless of the role. (line 150) 

    In the beginning of that function, the GAP role (periph or central) is checked, but it is not used as condition for the MTU exchange.

    It does not look like a "bug" to me, and in fact my connections are getting up quite OK. I do see some unexpected BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST event on the client side for each connection, but I just ignore it and it does not break anything.

    That being said, it seems a bit dumb that upon connecting, both nRF52 devices will request the MTU exchange. Even if it works, I find it a bit disturbing.

    I can easily fix this by editing the code in the SDK, but I would rather not poke with the library code directly. In the SDK there are compile options for just about every feature you can imagine, but for some reason this MTU exchange has been left as a code block that is always executed.

    Any comments from Nordic what is the recommended solution here to ensure maximum interoperability?

    Looking at the code in nrf_ble_gatt.c, it seems that it would be quite trivial to add compile time options for:

    • disable automatic MTU exchange request completely (if you e.g. want to call it explicitly from the app, after a delay)
    • disable/enable automatic MTU exchange in periheral role 
    • disable/enable automatic MTU exchange in central role 

     

    (If 2nd and 3rd options are available then the 1st is kind of redundant, but anyway...)

    EDIT: I'm talking about compile time options above, but would be just as happy to have some parameter or option that can be changed at runtime.  

  • TylerD said:
    disable automatic MTU exchange request completely

    The automatic MTU exchange is a bit poorly worded. There's no automation built in to the SoftDevice, both the MTU exchange requests and the response are controlled by the application.

    We've added the ability of the GATT library to automatically issue an MTU exchange request upon connection establishment for both central, peripheral, client, and server. 
    This is due to the fact that if you develop a peripheral server you have no guarantee that a connected central client will issue its own MTU exchange request. That is unless you develop both the peripheral AND the central. 


     

    TylerD said:

    Looking at the implementation in nrf_ble_gatt.c (function on_connected_evt())  it is clear that the MTU exchange is requested for any new connection, regardless of the role. (line 150) 

    In the beginning of that function, the GAP role (periph or central) is checked, but it is not used as condition for the MTU exchange.

    I suggest you use the role check if you control both sides of the link, if you only control the peripheral I would leave it as is, and maybe delay the request a bit. 

Related