当characteristic大小超过19 Bytes 时,ReadUsingCharUUID 无法取得正确的Response
最近在用cc2650学习蓝牙,想将char的大小从1Byte增大,理论上应该能增大到27-4-3-20 Byte。但是实际操作中超过了19Byte就会不能用UUID读取。但是如果手动输入handle值,还是能够顺利地进行操作的。
关于修改,我用Simple_Peripheral 修改了一下Char1的大小,从uint8_t 变成了uint8_t [20]
结果在用Simple_Central利用GATT_ReadUsingCharUUID()识别Peripheral的Char1时候就失败了。
然后改用BTool抓包得到了INVALID_HANDLE的错误码。
以下是BTool的抓包记录:
--------------------------------------------------------------------
[25] : <Tx> - 11:29:31.438
-Type : 0x01 (Command)
-OpCode : 0xFDB4 (GATT_ReadUsingCharUUID)
-Data Length : 0x08 (8) byte(s)
ConnHandle : 0x0000 (0)
StartHandle : 0x0001 (1)
EndHandle : 0xFFFF (65535)
Type : F1:FF
Dump(Tx):
0000:01 B4 FD 08 00 00 01 00 FF FF EE EE ............
--------------------------------------------------------------------
[26] : <Rx> - 11:29:31.474
-Type : 0x04 (Event)
-EventCode : 0x00FF (HCI_LE_ExtEvent)
-Data Length : 0x06 (6) bytes(s)
Event : 0x067F (1663) (GAP_HCI_ExtentionCommandStatus)
Status : 0x00 (0) (Success)
OpCode : 0xFDB4 (GATT_ReadUsingCharUUID)
DataLength : 0x00 (0)
Dump(Rx):
0000:04 FF 06 7F 06 00 B4 FD 00 .........
--------------------------------------------------------------------
[27] : <Rx> - 11:29:32.773
-Type : 0x04 (Event)
-EventCode : 0x00FF (HCI_LE_ExtEvent)
-Data Length : 0x0A (10) bytes(s)
Event : 0x0501 (1281) (ATT_ErrorRsp)
Status : 0x00 (0) (Success)
ConnHandle : 0x0000 (0)
PduLen : 0x04 (4)
ReqOpCode : 0x08 (ATT_ReadByTypeReq)
Handle : 0x001E (30)
ErrorCode : 0x01 (1) (INVALID_HANDLE)
The Attribute Handle Given Was Not
Valid On This Server.
Dump(Rx):
0000:04 FF 0A 01 05 00 00 00 04 08 2E 00 01 .............
--------------------------------------------------------------------
虽然返回的是错误的response,但是还是能看到,这条信息里返回了Char的handle,也算是发掘了一条怎么正统的后备选择。
同时,在Peripheral上,还用断点对 simpleProfile_ReadAttrCB() 进行观察。 发现传参maxLen的值正好为19。所以我特地将PDU的值从27增大到了30。但是在随后的几次断点测试中,不论PDU值为多少,maxLen的值始终为19。
所以关于这个maxLen的限制,逆向工程了以后,我自己的思考是:maxLen来自于gettserapp_util.c:
static bStatus_t gattServApp_SendNotiInd( uint16 connHandle, uint8 cccValue, uint8 authenticated, gattAttribute_t *pAttr, uint8 taskId, pfnGATTReadAttrCB_t pfnReadAttrCB ) { attHandleValueNoti_t noti; uint16 len; bStatus_t status; // If the attribute value is longer than (ATT_MTU - 3) octets, then // only the first (ATT_MTU - 3) octets of this attributes value can // be sent in a notification. noti.pValue = (uint8 *)GATT_bm_alloc( connHandle, ATT_HANDLE_VALUE_NOTI, GATT_MAX_MTU, &len ); if ( noti.pValue != NULL ) { status = (*pfnReadAttrCB)( connHandle, pAttr, noti.pValue, ¬i.len, //HACK: place fixed maxLen value 0, len, GATT_LOCAL_READ ); if ( status == SUCCESS ) { noti.handle = pAttr->handle; if ( cccValue & GATT_CLIENT_CFG_NOTIFY ) { status = GATT_Notification( connHandle, ¬i, authenticated ); } else // GATT_CLIENT_CFG_INDICATE { status = GATT_Indication( connHandle, (attHandleValueInd_t *)¬i, authenticated, taskId ); } } if ( status != SUCCESS ) { GATT_bm_free( (gattMsg_t *)¬i, ATT_HANDLE_VALUE_NOTI ); } } else { status = bleNoResources; } return ( status ); }
其中maxLen的值由GATT_bm_alloc在BLE STACK上分配空间后返回的大小。
这就令我很费解。尽管我增加了PDU的值,BLE STACK部分就是不愿意分配给我更多的空间。
附上Profile相关的代码。
#define SERVAPP_NUM_ATTR_SUPPORTED 20 /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // Simple GATT Profile Service UUID: 0xFFF0 CONST uint8 simpleProfileServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID) }; // Characteristic 1 UUID: 0xFFF1 CONST uint8 simpleProfilechar1UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR1_UUID), HI_UINT16(SIMPLEPROFILE_CHAR1_UUID) }; // Characteristic 2 UUID: 0xFFF2 CONST uint8 simpleProfilechar2UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR2_UUID), HI_UINT16(SIMPLEPROFILE_CHAR2_UUID) }; // Characteristic 3 UUID: 0xFFF3 CONST uint8 simpleProfilechar3UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR3_UUID), HI_UINT16(SIMPLEPROFILE_CHAR3_UUID) }; // Characteristic 4 UUID: 0xFFF4 CONST uint8 simpleProfilechar4UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR4_UUID), HI_UINT16(SIMPLEPROFILE_CHAR4_UUID) }; // Characteristic 5 UUID: 0xFFF5 CONST uint8 simpleProfilechar5UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR5_UUID), HI_UINT16(SIMPLEPROFILE_CHAR5_UUID) }; // Characteristic 6 UUID: 0xEEEE CONST uint8 simpleProfilechar6UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR6_UUID), HI_UINT16(SIMPLEPROFILE_CHAR6_UUID) }; /********************************************************************* * Profile Attributes - variables */ // Simple Profile Service attribute static CONST gattAttrType_t simpleProfileService = { ATT_BT_UUID_SIZE, simpleProfileServUUID }; // Simple Profile Characteristic 1 Properties static uint8 simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE_NO_RSP; // Characteristic 1 Value static uint8 simpleProfileChar1[SIMPLEPROFILE_CHAR1_LEN] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //FIXME change from uint8_t to uinr8_t[20] // Simple Profile Characteristic 1 User Description static uint8 simpleProfileChar1UserDesp[] = "CHAR1 20B"; // Simple Profile Characteristic 2 Properties static uint8 simpleProfileChar2Props = GATT_PROP_READ; // Characteristic 2 Value static uint8 simpleProfileChar2 = 0; // Simple Profile Characteristic 2 User Description static uint8 simpleProfileChar2UserDesp[17] = "Characteristic 2"; // Simple Profile Characteristic 3 Properties static uint8 simpleProfileChar3Props = GATT_PROP_WRITE; // Characteristic 3 Value static uint8 simpleProfileChar3 = 0; // Simple Profile Characteristic 3 User Description static uint8 simpleProfileChar3UserDesp[17] = "Characteristic 3"; // Simple Profile Characteristic 4 Properties static uint8 simpleProfileChar4Props = GATT_PROP_NOTIFY; // Characteristic 4 Value static uint8 simpleProfileChar4 = 0; // Simple Profile Characteristic 4 Configuration Each client has its own // instantiation of the Client Characteristic Configuration. Reads of the // Client Characteristic Configuration only shows the configuration for // that client and writes only affect the configuration of that client. static gattCharCfg_t *simpleProfileChar4Config; // Simple Profile Characteristic 4 User Description static uint8 simpleProfileChar4UserDesp[17] = "Characteristic 4"; // Simple Profile Characteristic 5 Properties static uint8 simpleProfileChar5Props = GATT_PROP_READ; // Characteristic 5 Value static uint8 simpleProfileChar5[SIMPLEPROFILE_CHAR5_LEN] = { 0, 0, 0, 0, 0 }; // Simple Profile Characteristic 5 User Description static uint8 simpleProfileChar5UserDesp[17] = "Characteristic 5"; // Simple Profile Characteristic 6 Properties static uint8 simpleProfileChar6Props = GATT_PROP_READ; // Characteristic 6 Value static uint8 simpleProfileChar6[SIMPLEPROFILE_CHAR6_LEN]; //FIXME change from uint8_t to uinr8_t[20] // Simple Profile Characteristic 6 User Description static uint8 simpleProfileChar6UserDesp[] = "***6***"; /********************************************************************* * Profile Attributes - Table */ static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] = { // Simple Profile Service { { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */ GATT_PERMIT_READ, /* permissions */ 0, /* handle */ (uint8 *)&simpleProfileService /* pValue */ }, // Characteristic 1 Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &simpleProfileChar1Props }, // Characteristic Value 1 { { ATT_BT_UUID_SIZE, simpleProfilechar1UUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, simpleProfileChar1 //FIXME: from variable address to array pointer }, // Characteristic 1 User Description { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, simpleProfileChar1UserDesp }, // Characteristic 2 Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &simpleProfileChar2Props }, // Characteristic Value 2 { { ATT_BT_UUID_SIZE, simpleProfilechar2UUID }, GATT_PERMIT_READ, 0, &simpleProfileChar2 }, // Characteristic 2 User Description { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, simpleProfileChar2UserDesp }, // Characteristic 3 Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &simpleProfileChar3Props }, // Characteristic Value 3 { { ATT_BT_UUID_SIZE, simpleProfilechar3UUID }, GATT_PERMIT_WRITE, 0, &simpleProfileChar3 }, // Characteristic 3 User Description { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, simpleProfileChar3UserDesp }, // Characteristic 4 Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &simpleProfileChar4Props }, // Characteristic Value 4 { { ATT_BT_UUID_SIZE, simpleProfilechar4UUID }, 0, 0, &simpleProfileChar4 }, // Characteristic 4 configuration { { ATT_BT_UUID_SIZE, clientCharCfgUUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8 *)&simpleProfileChar4Config }, // Characteristic 4 User Description { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, simpleProfileChar4UserDesp }, // Characteristic 5 Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &simpleProfileChar5Props }, // Characteristic Value 5 { { ATT_BT_UUID_SIZE, simpleProfilechar5UUID }, GATT_PERMIT_AUTHEN_READ, 0, simpleProfileChar5 }, // Characteristic 5 User Description { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, simpleProfileChar5UserDesp }, // Characteristic 6 Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &simpleProfileChar6Props }, // Characteristic 6 Value { { ATT_BT_UUID_SIZE, simpleProfilechar6UUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, simpleProfileChar6 //FIXME: from variable address to array pointer }, // Characteristic 6 User Description { { ATT_BT_UUID_SIZE, charUserDescUUID }, GATT_PERMIT_READ, 0, simpleProfileChar6UserDesp }, }; /********************************************************************* * @fn SimpleProfile_SetParameter * * @brief Set a Simple Profile parameter. * * @param param - Profile parameter ID * @param len - length of data to write * @param value - pointer to data to write. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). * * @return bStatus_t */ bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value ) { bStatus_t ret = SUCCESS; switch ( param ) { case SIMPLEPROFILE_CHAR1: if ( len == SIMPLEPROFILE_CHAR1_LEN ) //FIXME: fixed 20-byte size to be set { //simpleProfileChar1 = *((uint8*)value); VOID memcpy( simpleProfileChar1, value, SIMPLEPROFILE_CHAR1_LEN ); } else { ret = bleInvalidRange; } break; case SIMPLEPROFILE_CHAR2: if ( len == sizeof ( uint8 ) ) { simpleProfileChar2 = *((uint8*)value); } else { ret = bleInvalidRange; } break; case SIMPLEPROFILE_CHAR3: if ( len == sizeof ( uint8 ) ) { simpleProfileChar3 = *((uint8*)value); } else { ret = bleInvalidRange; } break; case SIMPLEPROFILE_CHAR4: if ( len == sizeof ( uint8 ) ) { simpleProfileChar4 = *((uint8*)value); // See if Notification has been enabled GATTServApp_ProcessCharCfg( simpleProfileChar4Config, &simpleProfileChar4, FALSE, simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ), INVALID_TASK_ID, simpleProfile_ReadAttrCB ); } else { ret = bleInvalidRange; } break; case SIMPLEPROFILE_CHAR5: if ( len == SIMPLEPROFILE_CHAR5_LEN ) { VOID memcpy( simpleProfileChar5, value, SIMPLEPROFILE_CHAR5_LEN ); } else { ret = bleInvalidRange; } break; default: ret = INVALIDPARAMETER; break; case SIMPLEPROFILE_CHAR6: if ( len == SIMPLEPROFILE_CHAR6_LEN ) //FIXME: fixed 20-byte size to be set { //simpleProfileChar1 = *((uint8*)value); VOID memcpy( simpleProfileChar6, value, SIMPLEPROFILE_CHAR6_LEN ); } else { ret = bleInvalidRange; } break; } return ( ret ); } /********************************************************************* * @fn SimpleProfile_GetParameter * * @brief Get a Simple Profile parameter. * * @param param - Profile parameter ID * @param value - pointer to data to put. This is dependent on * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast to * uint16 pointer). * * @return bStatus_t */ bStatus_t SimpleProfile_GetParameter( uint8 param, void *value ) { bStatus_t ret = SUCCESS; switch ( param ) { case SIMPLEPROFILE_CHAR1: VOID memcpy( value, simpleProfileChar1, SIMPLEPROFILE_CHAR1_LEN ); //FIXME: fixed 20-byte size to be fetched break; case SIMPLEPROFILE_CHAR2: *((uint8*)value) = simpleProfileChar2; break; case SIMPLEPROFILE_CHAR3: *((uint8*)value) = simpleProfileChar3; break; case SIMPLEPROFILE_CHAR4: *((uint8*)value) = simpleProfileChar4; break; case SIMPLEPROFILE_CHAR5: VOID memcpy( value, simpleProfileChar5, SIMPLEPROFILE_CHAR5_LEN ); break; case SIMPLEPROFILE_CHAR6: VOID memcpy( value, simpleProfileChar6, SIMPLEPROFILE_CHAR6_LEN ); //FIXME: fixed 20-byte size to be fetched break; default: ret = INVALIDPARAMETER; break; } return ( ret ); } /********************************************************************* * @fn simpleProfile_ReadAttrCB * * @brief Read an attribute. * * @param connHandle - connection message was received on * @param pAttr - pointer to attribute * @param pValue - pointer to data to be read * @param pLen - length of data to be read * @param offset - offset of the first octet to be read * @param maxLen - maximum length of data to be read * @param method - type of read message * * @return SUCCESS, blePending or Failure */ static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method) { bStatus_t status = SUCCESS; //maxLen = 20; // Make sure it's not a blob operation (no attributes in the profile are long) if ( offset > 0 ) { return ( ATT_ERR_ATTR_NOT_LONG ); } if ( pAttr->type.len == ATT_BT_UUID_SIZE ) { // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { // No need for "GATT_SERVICE_UUID" or "GATT_CLIENT_CHAR_CFG_UUID" cases; // gattserverapp handles those reads // characteristics 1 and 2 have read permissions // characteritisc 3 does not have read permissions; therefore it is not // included here // characteristic 4 does not have read permissions, but because it // can be sent as a notification, it is included here case SIMPLEPROFILE_CHAR1_UUID: //FIXME: fixed 20-byte size to be read *pLen = SIMPLEPROFILE_CHAR1_LEN; memset(pValue, 0, 20); VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR1_LEN ); break; case SIMPLEPROFILE_CHAR2_UUID: case SIMPLEPROFILE_CHAR4_UUID: *pLen = 1; pValue[0] = *pAttr->pValue; break; case SIMPLEPROFILE_CHAR5_UUID: *pLen = SIMPLEPROFILE_CHAR5_LEN; VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR5_LEN ); break; case SIMPLEPROFILE_CHAR6_UUID: //FIXME: fixed 20-byte size to be read *pLen = SIMPLEPROFILE_CHAR6_LEN; VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR6_LEN ); break; default: // Should never get here! (characteristics 3 and 4 do not have read permissions) *pLen = 0; status = ATT_ERR_ATTR_NOT_FOUND; break; } } else { // 128-bit UUID *pLen = 0; status = ATT_ERR_INVALID_HANDLE; } return ( status ); } /********************************************************************* * @fn simpleProfile_WriteAttrCB * * @brief Validate attribute data prior to a write operation * * @param connHandle - connection message was received on * @param pAttr - pointer to attribute * @param pValue - pointer to data to be written * @param len - length of data * @param offset - offset of the first octet to be written * @param method - type of write message * * @return SUCCESS, blePending or Failure */ static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr, uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method) { bStatus_t status = SUCCESS; uint8 notifyApp = 0xFF; if ( pAttr->type.len == ATT_BT_UUID_SIZE ) { // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { case SIMPLEPROFILE_CHAR1_UUID: //FIXME: write 20-byte value to attribute in writeCB if ( offset == 0 ) { if ( len != SIMPLEPROFILE_CHAR1_LEN ) { status = ATT_ERR_INVALID_VALUE_SIZE; } }else { status = ATT_ERR_ATTR_NOT_LONG; } //Write the value if ( status == SUCCESS ) { //uint8 *pCurValue = (uint8 *)pAttr->pValue; VOID memcpy( pAttr->pValue, pValue, SIMPLEPROFILE_CHAR1_LEN ); notifyApp = SIMPLEPROFILE_CHAR1; } break; case SIMPLEPROFILE_CHAR3_UUID: //Validate the value // Make sure it's not a blob oper if ( offset == 0 ) { if ( len != 1 ) { status = ATT_ERR_INVALID_VALUE_SIZE; } } else { status = ATT_ERR_ATTR_NOT_LONG; } //Write the value if ( status == SUCCESS ) { uint8 *pCurValue = (uint8 *)pAttr->pValue; *pCurValue = pValue[0]; notifyApp = SIMPLEPROFILE_CHAR3; } break; case SIMPLEPROFILE_CHAR6_UUID: //FIXME: write 20-byte value to attribute in writeCB if ( offset == 0 ) { if ( len != SIMPLEPROFILE_CHAR6_LEN ) { status = ATT_ERR_INVALID_VALUE_SIZE; } }else { status = ATT_ERR_ATTR_NOT_LONG; } //Write the value if ( status == SUCCESS ) { //uint8 *pCurValue = (uint8 *)pAttr->pValue; VOID memcpy( pAttr->pValue, pValue, SIMPLEPROFILE_CHAR6_LEN ); notifyApp = SIMPLEPROFILE_CHAR6; } break; case GATT_CLIENT_CHAR_CFG_UUID: status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_NOTIFY ); break; default: // Should never get here! (characteristics 2 and 4 do not have write permissions) status = ATT_ERR_ATTR_NOT_FOUND; break; } } else { // 128-bit UUID status = ATT_ERR_INVALID_HANDLE; } // If a characteristic value changed then callback function to notify application of change if ( (notifyApp != 0xFF ) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange ) { simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp ); } return ( status ); } /********************************************************************* *********************************************************************/