Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HCI transport layer limits MTU length to uint8_t, so characteristic values greater than 255 don't get written #203

Open
ricozinn opened this issue Sep 24, 2021 · 0 comments

Comments

@ricozinn
Copy link

I tried posting this problem here first...
https://forum.arduino.cc/t/arduino-ble-getting-the-most-out-of-ble-5-0/908559

I'm using this library on the Artemis (Ambiq, Apollo3, Cortex M4) and although this library lets you create your peripheral characteristic to be 512 bytes, the HCI layer uses uint8_t, so any lengths over 255 get truncated when you transmit them (as can be seen when you sniff it out with Wireshark, or just count the bytes on the central/receiving end).

In the code below where it does the while (_pendingPkt >= _maxPkt) ... both of those are uint8_t, so it won't ever send the full 512 our characteristic value holds. Both of these are harder to fix since we probably have to change things in the mbed OS cordio library (which is I think a linked library, not something that compiles when you build your arduino project).

//from HCI.cpp
int HCIClass::sendAclPkt(uint16_t handle, uint8_t cid, uint8_t plen, void* data)
{
  while (_pendingPkt >= _maxPkt) {// problem is here...
    poll();
  }

  struct __attribute__ ((packed)) HCIACLHdr {
    uint8_t pktType;
    uint16_t handle;
    uint16_t dlen;
    uint16_t plen;
    uint16_t cid;
  } aclHdr = { HCI_ACLDATA_PKT, handle, uint8_t(plen + 4), plen, cid };

  uint8_t txBuffer[sizeof(aclHdr) + plen];
  memcpy(txBuffer, &aclHdr, sizeof(aclHdr));
  memcpy(&txBuffer[sizeof(aclHdr)], data, plen);

  if (_debug) {
    dumpPkt("HCI ACLDATA TX -> ", sizeof(aclHdr) + plen, txBuffer);
  }

  _pendingPkt++;
  HCITransport.write(txBuffer, sizeof(aclHdr) + plen);

  return 0;
}

Notice the setMaxMtu() uses pktLen, which is uint16_t, but maxPkt is uint8_t, so again because of this we'll never be able to negotiate an MTU with the central greater than 255 - 9.
int HCIClass::readLeBufferSize(uint16_t& pktLen, uint8_t& maxPkt)

//Also from HCI.cpp
{
  int result = sendCommand(OGF_LE_CTL << 10 | OCF_LE_READ_BUFFER_SIZE);

  if (result == 0) {
    struct __attribute__ ((packed)) HCILeBufferSize {
      uint16_t pktLen;
      uint8_t maxPkt;
    } *leBufferSize = (HCILeBufferSize*)_cmdResponse;

    pktLen = leBufferSize->pktLen;
    _maxPkt = maxPkt = leBufferSize->maxPkt;

#ifndef __AVR__
    ATT.setMaxMtu(pktLen - 9); // max pkt len - ACL header sizesize
#endif
  }

  return result;
}

What I would expect/hope for is that I could create the characteristic to have 512 bytes, and then write 512 bytes to that characteristic and if for whatever reason the negotiated MTU is less than 512 it uses multiple packets or connection intervals and keeps sending bytes until it sends the full 512 bytes instead of truncating it.

Thanks for any help or ideas if I'm understanding this wrong...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant