if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)))) {
perror(\ exit(1); }
dl->dev_num = HCI_MAX_DEV; dr = dl->dev_req;
//1. 打开一个HCI socket.
if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) { perror(\ exit(1); }
// 2. 使用HCIGETDEVLIST,得到所有dongle的Device ID。存放在dl中。 if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) { perror(\ exit(1); }
// 3 使用HCIGETDEVINFO,得到对应Device ID的Dongle信息。 di.dev_id = (dr+i)->dev_id;
ioctl(ctl, HCIGETDEVINFO, (void *) &di); 这样就能得到所有Dongle信息。
struct hci_dev_info {
uint16_t dev_id; //dongle Device ID char name[8]; //Dongle name
bdaddr_t bdaddr; //Dongle bdaddr
uint32_t flags; //Dongle Flags:如:UP,RUNING,Down等。
uint8_t type; //Dongle连接方式:如USB,PC Card,UART,RS232等。
uint8_t features[8];
uint32_t pkt_type; uint32_t link_policy; uint32_t link_mode;
uint16_t acl_mtu; uint16_t acl_pkts; uint16_t sco_mtu; uint16_t sco_pkts;
struct hci_dev_stats stat; //此Dongle的数据信息,如发送多少个ACL Packet,正确多少,错误多少,等等。
};
b. 打开一个HCI Socket---int hci_open_dev(int dev_id)
这个function用来打开一个HCI Socket。它首先打开一个HCI protocol的Socket(房间),并将此Socket与device ID=参数dev_id的Dongle绑定起来。只有bind后,它才将Socket句柄与Dongle对应起来。
注意,所有的HCI Command发送之前,都需要使用 hci_open_dev打开并绑定。
c. 关闭一个HCI Socket
int hci_close_dev(int dd) //简单的关闭使用hci_open_dev打开的Socket。
d. 向HCI Socket(对应一个Dongle)发送 request
int hci_send_req(int dd, struct hci_request *r, int to)
BlueZ提供这个function非常有用,它可以实现一切Host向Modules发送Command的功能。 参数1:HCI Socket。 参数2:Command内容。
参数3:以milliseconds为单位的timeout. 下面详细解释此function和用法:
当应用程序需要向Dongle(对应为一个bind后的Socket)发送Command时,调用此function.
其中,参数一dd对应一个使用hci_open_dev()打开的Socket(Dongle)。 参数三to则为等待Dongle执行并回复命令结果的timeout.以毫秒为单位。 参数二hci_request * r 最为重要,首先看它的结构: struct hci_request {
uint16_t ogf; //Opcode Group uint16_t ocf; //Opcode Command
int event; //此Command产生的Event类型。 void *cparam; //Command 参数 int clen; //Command参数长度 void *rparam; //Response 参数 int rlen; //Response 参数长度 };
ogf,ocf不用多说,对应前面的图就明白这是Group Code和Command Code。这两项先确定下来,然后可以查HCI Spec。察看输入参数(cparam)以及输出参数(rparam)含义。至于他们的结构以及参数长度,则在~/include/net/bluetooth/hci.h中有定义。
例1:得到某个连接的Policy Setting.
HCI Spec以及~/include/net/bluetooth/hci.h中均可看到,OGF=OGF_LINK_POLICY(0x02). OCF=OCF_READ_LINK_POLICY(0x0C). 因为这个Command用来读取某个ACL连接的Policy Setting。所以输入参数即为此连接Handle.
返回参数则包含3部分,status(Command是否顺利执行), handle(连接Handle)。 policy(得到的policy值)
这就又引入了一个新问题,如何得到某个ACL连接的Handle。
可以使用ioctl HCIGETCONNINFO得到ACL 连接Handle。 ioctl(dd, HCIGETCONNINFO, (unsigned long) cr); Connect_handle = htobs(cr->conn_info->handle); 所以完整的过程如下:
struct hci_request HCI_Request;
read_link_policy_cp Command_Param; read_link_policy_rp Response_Param; // 1.得到ACL Connect Handle
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
return -1; }
Connect_handle = htobs(cr->conn_info->handle); memset(&HCI_Request, 0, sizeof(HCI_Request));
memset(&Command_Param, 0 , sizeof(Command_Param)); memset(&Response_Param, 0 , sizeof(Response_Param)); // 2.填写Command输入参数
Command_Param.handle = Connect_handle;
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) < 0) {
perror(\ return -1; }
//如果返回值状态不对
if (Response_Param.status) { return -1; }
//得到当前policy
*policy = Response_Param.policy; e. 几个更基础的function
static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) //bdaddr copy
static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)//bdaddr 比较 f. 得到指定Dongle BDAddr
int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。 参数2:输出参数,其中会放置bdaddr. 参数3:以milliseconds为单位的timeout.
g. 读写Dongle Name
int hci_read_local_name(int dd, int len, char *name, int to) int hci_write_local_name(int dd, const char *name, int to)
参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。 参数2:读取或设置Name。
参数3:以milliseconds为单位的timeout. 注意:这里的Name与IOCTL HCIGETDEVINFO 得到hci_dev_info中的name不同
h. 得到HCI Version
int hci_read_local_version(int dd, struct hci_version *ver, int to) i. 得到已经UP的Dongle BDaddr
int hci_devba(int dev_id, bdaddr_t *bdaddr); dev_id: Dongle Device ID.
bdaddr:输出参数,指定Dongle如果UP, 则放置其BDAddr j. 得到BDADDR不等于参数bdaddr的Dongle Device ID
int hci_get_route(bdaddr_t *bdaddr)
查找Dongle,发现Dongle Bdaddr不等于参数bdaddr的第一个Dongle,则返回此Dongle Device ID。
所以,如果: int hci_get_route(NULL),则得到第一个可用的Dongle Device ID。 k. 将BDADDR转换为字符串
int ba2str(const bdaddr_t *ba, char *str)
对远程dongle进行操作:
1. inquiry 远程Bluetooth Device
int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **ii, long flags)
hci_inquiry()用来命令指定的Dongle去搜索周围所有bluetooth device.并将搜索到的Bluetooth Device bdaddr 传递回来。
参数1:dev_id:指定Dongle Device ID。如果此值小于0,则会使用第一个可用的Dongle。
参数2:len: 此次inquiry的时间长度(每增加1,则增加1.25秒时间) 参数3:nrsp:此次搜索最大搜索数量,如果给0。则此值会取255。
参数4:lap:BDADDR中LAP部分,Inquiry时这块值缺省为0X9E8B33.通常使用NULL。则自动设置。
参数5:ii:存放搜索到Bluetooth Device的地方。给一个存放inquiry_info指针的地址,它会自动分配空间。并把那个空间头地址放到其中。
参数6:flags:搜索flags.使用IREQ_CACHE_FLUSH,则会真正重新inquiry。否则可能会传回上次的结果。
返回值是这次Inquiry到的Bluetooth Device 数目。
注意:如果*ii不是自己分配的,而是让hci_inquiry()自己分配的,则需要调用bt_free()来帮它释放空间。
2. 得到指定BDAddr的reomte device Name
int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int
to)
参数1:使用hci_open_dev()打开的Socket。 参数2:对方BDAddr. 参数3:name 长度。
参数4:(out)放置name的位置。 参数5:等待时间。 3. 读取连接的信号强度
int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to) 注意,所有对连接的操作,都会有一个参数,handle.这个参数是连接的Handle。 前面讲过如何得到连接Handle的。
3.3、L2CAP层编程 3.3.1、L2CAP协议简介
Logical Link Control and Adaptation Protocol(L2CAP)逻辑连接控制和适配协议为上层协
议提供面向连接和无连接的数据服务,并提供多协议功能和分割重组操作。L2CAP 充许上层协议和应用软件传输和接收最大长度为 64K 的 L2CAP 数据包。
L2CAP 基于通道(channel) 的概念。 通道 (Channel) 是位于基带 (baseband) 连接之上的逻辑连接。每个通道以多对一的方式绑定一个单一协议 (single protocol)。多个通道可以绑定同一个协议,但一个通道不可以绑定多个协议。 每个在通道里接收到的 L2CAP 数据包被传到相应的上层协议。 多个通道可共享同一个基带连接。也就是说,所有L2CAP数据均通过HCI传输到Remote Device。且上层协议的数据,大都也通过L2CAP来传送。L2CAP可以发送Command。例如连接,断连等等。
3.3.2、L2CAP编程方法
L2CAP编程非常重要,它和HCI基本就是Linux Bluetooth编程的基础了。几乎所有协议的连接,断连,读写都是用L2CAP连接来做的。 1.创建L2CAP Socket
socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
domain=PF_BLUETOOTH, type可以是多种类型。protocol=BTPROTO_L2CAP. 2.绑定:
// Bind to local address
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
bacpy(&addr.l2_bdaddr, &bdaddr); //bdaddr为本地Dongle BDAddr if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror(\ goto error; } 3.连接