本文作者的博客地址:http://blog.sina.com.cn/samzhen1977
作者:Sam (甄峰)
sam_code@hotmail.com
背景:
Sam在做Bluetooth USB Dongle与Bluetooth Remote
Control连接的程序时。因为Bluetooth Remote Control会在连接后发送 sniff
request.约定双方的数据频率为5ms.但因为Linux程序这端没有处理Sniff(缺省为disable).
所以这个request没有成功。造成双方的数据传输并不是定时传输,而是当channel忙时发数据少,channel不忙时发数据多。于是空中鼠标非
常不稳定。Ray要求添加1.Enable
Sniff. 2. set Sniff mode.
Sam于是创建了三个接口:
1. Read_LINK_POLICY() //read Link policy. sniff包含在其中
2. Wiret_LINK_POLICY() //write Link policy.
3. Set_Sniff_Mode() //设置sniff
如前面Blog中所说:可以使用BlueZ提供的Interface发送HCI Command。
int hci_send_req(int dd,
struct hci_request *r, int to)
1.
Command知识学习:
Sam首先查看:Host Controller Interface
Functional Specification文档中HCI Commands and
Events.->Link Policy
Commands->Read
Link Policy Settings Command
.
Command:
HCI_Read_Link_Policy_Settings
OCF: 0x000C
Command Parameters:
Connection_Handle
Return Parameters: Status,
Connection_Handle Link_Policy_Settings
说明:这个Command 从指定的Connection Handle读取Link Policy 设置。这个Connection
Handle必须是ACL连接的。
Command
Parameters:
1. Connection_Handle(Size:2 Octets):一个连接的句柄。
Retrun Parameters:
返回参数
返回参数1. Status: 0x00: 读取成功
0x01-0xFF: 可以从Error Code表中找到错误含义。
返回参数2. Connection_Handle: 一个连接的句柄。
返回参数3. Link_Policy_Settings:
0x0000: Disable All LM Modes Default.
0x0001: Enable Role Switch
0x0002: Enable Hold Mode.
0x0004: Enable Sniff Mode.
0x0008: Enable Park State.
Event
Generate:
当 Read_link_Policy_Settings命令执行完毕时,会发送一个 Command Complete
Event.
2.具体编程
Sam发现BlueZ提供的用户层Interface中(也就是BlueZ-lib中)并没有直接提供发送命令--Read Link
Policy Settings 的接口。但提供了发送Read Remote Version
Information的接口。这就很简单了,直接仿照这个接口写一个function即可。
BlueZ-lib中的Read Remote Version Information function:
int hci_read_remote_version(int dd, uint16_t handle, struct
hci_version *ver, int to)
这个function的用法可以参考BlueZ-uitil。
参数描述:
int dd: 由hci_open_dev()返回的HCI Socket 句柄。
uint16_t handle: 一个连接的句柄。
struct hci_version *ver: 返回的版本结构体。
int to: 等待的时间。
这个function 的核心就是前文介绍的:hci_send_req(dd,
&rq,
to)
只需要读懂它的参数配置,我们的仿造function就算成功了。
其中,dd就是hci_open_dev() 返回的HCI Socket Handle。 to 就是等待时间。
重点是hci_request * rq
这个结构体如下:
struct hci_request
{
uint16_t ogf; //Command组编号
uint16_t ocf; // Command编号 。由这两个ID号组合,唯一确定Command.
int event; // Command 成功后将自动发送这个Event。
void *cparam; //Command
Param.命令参数。
int clen; //命令参数长度
void *rparam; //response Param。返回参数
int rlen; //返回参数长度
};
其中
ogf, ocf很明显,填上OGF_LINK_POLICY,OCF_READ_LINK_POLICY就可以了。
event:因为文档中讲此Command成功的话,就会发送Command Complete Event. 所以不用指定了。
clen: 文档中讲了:2个字节。 BlueZ中hci.h中也指定了:
#define READ_LINK_POLICY_CP_SIZE 2
rparam: response 参数:文档中有讲,包括三项:BlueZ 中hci.h也定义了:
typedef struct
{
uint8_t status;
uint16_t handle;
uint16_t policy;
} __attribute__ ((packed)) read_link_policy_rp;
rlen: response 参数长度:#define READ_LINK_POLICY_RP_SIZE 5
cparam:Command
参数:如文档中所讲,结构体如下:
typedef struct
{
uint16_t handle;
} __attribute__ ((packed)) read_link_policy_cp;
综上所述,可以如下设置:
BTX_Rel hci_Read_Link_Policy(int dd, uint16_t handle, uint16_t
*policy, int to)
{
struct hci_request HCI_Request;
read_link_policy_cp Command_Param;
read_link_policy_rp Response_Param;
memset(&HCI_Request, 0, sizeof(HCI_Request));
memset(&Command_Param, 0 ,
sizeof(Command_Param));
memset(&Response_Param, 0 ,
sizeof(Response_Param));
// 1.
set Command Param Command_Param.handle = handle;
// 2.
set hci_request HCI_Request.ogf = OGF_LINK_POLICY;
//Command组ID
HCI_Request.ocf = OCF_READ_LINK_POLICY; //Command ID
HCI_Request.cparam = &Command_Param;
HCI_Request.clen = READ_LINK_POLICY_CP_SIZE;
HCI_Request.rparam = &Response_Param;
HCI_Request.rlen = READ_LINK_POLICY_RP_SIZE;
if (hci_send_req(dd, &HCI_Request, to)
{
perror("/nhci_send_req()");
return BTX_SEND_CMD_TO_REMOTE_ERR;
}
if (Response_Param.status)
{ return BTX_SEND_CMD_TO_REMOTE_ERR; }
*policy = Response_Param.policy;
return BTX_SUCCESS;
}
3. 用法:
1. hci socket的得到:
dd = hci_open_dev(dev_id);
if (dd < cr ="
malloc(sizeof(*cr)">bdaddr,
&des_bdaddr);
cr->type = ACL_LINK;
}
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) <
connect_handle ="
htobs(cr-">conn_info->handle);
3.调用
Rel_get_policy = hci_Read_Link_Policy(dd, Connect_handle,
&polidy, 1000);
if(Rel_get_policy != BTX_SUCCESS)
{
printf("/nRead Link Policy Error[%d]", Rel_get_policy);
}
else
{
printf("/nPolicy is:[0x%x]", polidy);
}
4.关闭HCI Socket(这一步一定要做)
hci_close_dev(dd);