2008年12月3日星期三

通过Bluez提供的Interface发送HCI Command

作者: 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);

没有评论: