当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 );
}
/*********************************************************************
*********************************************************************/
