BT_PAN
PAN(Personal Area Networking Profile)即个人局域网协议。它描述了两台或多台支持蓝牙的设备如何组成一个自组网络,以及如何使用相同的机制通过网络接入点访问远程网络。 PAN Profile中主要包含的角色是:
网络接入点NAP(Network Access Point)
支持网络接入点(NAP)的蓝牙设备是是一种为支持网络服务,提供以太网桥的一些功能的蓝牙设备。具有NAP服务的设备在每个连接的蓝牙设备之间转发网络数据包。NAP与PANU之间使用蓝牙网络封装协议(BNEP)交换数据。支持NAP服务的设备可以允许一个或多个连接设备访问网络。
群自组网络GN(Group Ad-hoc Network)
群自组网允许移动主机协同创建自组网无线网络,而不使用额外的网络硬件。一个群自组网由一个蓝牙设备作为一个主节点操作,与作为从节点操作的1到7个蓝牙设备进行通信。GN与PANU之间使用蓝牙网络封装协议(BNEP)交换数据。此外,可能还有更多的非活动的群组网成员处于park mode。蓝牙强制执行了一个群自组网中只能有7个活动从设备的限制。
个人局域网用户PANU(Personal Area Network User)
PANU是使用NAP或GN服务的蓝牙设备。PANU支持作为NAP和GN角色的客户端角色,或者直接进行PANU到PANU的通信。
本文档主要是基于Sifli SDK,介绍作为PANU角色如何使用bt pan的基本功能。 涉及文件如下:
bts2_app_interface
bts2_app_pan
bt_pan初始化
pan初始化的函数:bt_pan_init,pan相关的状态、标志赋初始值
pan服务使能的函数:bt_pan_reg,使能pan profile
void bt_pan_init(bts2_app_stru *bts2_app_data)
{
U8 i;
bts2_app_data->pan_inst_ptr = &bts2_app_data->pan_inst;
//赋值pan的初始状态
bts2_app_data->pan_inst.pan_st = PAN_REG_ST;
//每一个pan连接会分配一个id
bts2_app_data->pan_inst.id = 0xffff;
//初始化本地角色和对端角色
bts2_app_data->pan_inst.local_role = PAN_NO_ROLE;
bts2_app_data->pan_inst.rmt_role = PAN_NO_ROLE;
bts2_app_data->pan_inst.mode = ACT_MODE;
//初始化pan的sdp内容,主要是用于判断是否在做sdp查询
//PAN_MAX_NUM是可以配置的最大支持sdp查询的数量
for (i = 0; i < PAN_MAX_NUM; i++)
{
bd_set_empty(&(bts2_app_data->pan_inst.pan_sdp[i].bd_addr));
bts2_app_data->pan_inst.pan_sdp[i].gn_sdp_pending = FALSE;
bts2_app_data->pan_inst.pan_sdp[i].gn_sdp_fail = FALSE;
bts2_app_data->pan_inst.pan_sdp[i].nap_sdp_pending = FALSE;
bts2_app_data->pan_inst.pan_sdp[i].nap_sdp_fail = FALSE;
}
}
void bt_pan_reg(bts2_app_stru *bts2_app_data)
{
bts2_pan_inst_data *ptr = NULL;
ptr = bts2_app_data->pan_inst_ptr;
//需要判断pan的状态,PAN_REG_ST为初始化状态,pan状态定义参考@bts2_pan_st
if (ptr->pan_st == PAN_REG_ST)
{
pan_reg_req(bts2_task_get_app_task_id(), bts2_task_get_pan_task_id(), bts2_app_data->local_bd);
//切换为PAN_IDLE_ST状态
ptr->pan_st = PAN_IDLE_ST;
//enable主要是完成本地sdp的注册
bt_pan_enable(bts2_app_data);
USER_TRACE(">> PAN register start\n");
}
else
{
USER_TRACE(">> PAN register fail\n");
}
}
typedef enum
{
PAN_IDLE_ST, /* 正在enable或者已经enable完成的状态 */
PAN_REG_ST, /* 初始化状态 */
PAN_SDS_REG_ST, /* 正在注册本端的sdp内容 */
PAN_BUSY_ST /* 已经连上pan的状态 */
} bts2_pan_st;
pan连接设备
以下流程描述了PANU如何发现、连接和使用NAP及其网络服务的过程。
第一步是发现周边可以使用的NAP设备。为此,PANU可以对附近的设备执行搜索,然后使用SDP从那些支持NAP角色的设备中检索NAP服务。(注意:支持NAP的设备必须在class of device里的service class字段里包含Networking位)
选择一个NAP设备进行连接。选择需要连接的设备发起ACL连接,如果NAP设备同时连接了多个PANU设备,则可能需要在连上ACL后做role switch。
BNEP连接。一旦创建完成了ACL连接,PANU应启动BNEP的L2CAP连接。BNEP连接过程包括所需的BNEP控制命令和可选的分组过滤器设置。如果NAP支持过滤,则应为每个连接存储所有已接受的网络数据包类型过滤器。
网络数据包交互。NAP应将网络数据包转发到已连接的PANU设备。这与网络桥接器的行为类似。
PANU或NAP可以随时终止连接。
pan连接设备接口为:
bts2_app_interface连接接口:bt_interface_conn_ext
bts2_app_pan连接接口:bt_pan_conn
/**
* @brief Initiate connect with the specified device and profile(hf sink)
* @param[in] mac Remote device address
* @param[in] ext_profile Profile value
*
* @return bt_err_t
**/
bt_err_t bt_interface_conn_ext(unsigned char *mac, bt_profile_t ext_profile);
//profile definition
typedef enum
{
BT_PROFILE_HFP = 0, /* HFP Profile */
BT_PROFILE_AVRCP, /* AVRCP Profile */
BT_PROFILE_A2DP, /* A2DP Profile */
BT_PROFILE_PAN, /* PAN Profile */
BT_PROFILE_HID, /* HID Profile */
BT_PROFILE_AG, /* AG Profile */
BT_PROFILE_SPP, /* SPP Profile */
BT_PROFILE_BT_GATT, /* BT_GATT Profile */
BT_PROFILE_PBAP, /* PBAP Profile */
BT_PROFILE_MAX
} bt_profile_t;
//调用这个接口之前会把mac转换为BTS2S_BD_ADDR蓝牙地址格式
void bt_pan_conn(BTS2S_BD_ADDR *bd)
{
bts2_app_stru *bts2_app_data = getApp();
bts2_pan_inst_data *ptr = NULL;
U8 idx, idx2;
ptr = bts2_app_data->pan_inst_ptr;
USER_TRACE(" pan st %x\n", ptr->pan_st);
//检查state是不是PAN_IDLE_ST
if (ptr->pan_st == PAN_IDLE_ST)
{
//判断是不是和想要连接的设备已经在连接pan了
idx = bt_pan_get_idx_by_bd(bts2_app_data, bd);
if (idx != PAN_MAX_NUM)
{
//正在做sdp查询了,完成之后会发起pan连接
if (ptr->pan_sdp[idx].gn_sdp_pending == TRUE)
{
USER_TRACE("SDP is in progress,connect pan later\n");
}
else
{
//发起PAN NAP角色的sdp查询,主要是检查对端是否支持PAN NAP
//做完sdp的查询过程会接着触发pan的连接过程
ptr->pan_sdp[idx].gn_sdp_pending = TRUE;
pan_svc_srch_req(bts2_task_get_app_task_id(), bd, PAN_NAP_ROLE);
USER_TRACE(">> PAN connect\n");
}
}
else
{
idx2 = bt_pan_get_idx(bts2_app_data);
//不是正在连接的设备则分配一个新的
if (idx2 != PAN_MAX_NUM)
{
//发起PAN NAP角色的sdp查询,主要是检查对端是否支持PAN NAP
//做完sdp的查询过程会接着触发pan的连接过程
bd_copy(&(ptr->pan_sdp[idx2].bd_addr), bd);
ptr->pan_sdp[idx2].gn_sdp_pending = TRUE;
pan_svc_srch_req(bts2_task_get_app_task_id(), bd, PAN_NAP_ROLE);
USER_TRACE(">> PAN connect\n");
}
else
{
//没有资源可以用了,都是正在连接别的设备
USER_TRACE("No pan resources are available\n");
}
}
}
else
{
//状态不对,连接失败
USER_TRACE(">> PAN connect fail\n");
}
}
pan连接断开设备接口为:
bts2_app_interface断开连接接口:bt_interface_disc_ext
bts2_app_pan断开接口:bt_pan_disc
//错误码定义
typedef enum
{
BT_EOK = 0,
/* general error code */
BT_ERROR_INPARAM = 0x10000001, /**参数错误*/
BT_ERROR_UNSUPPORTED = 0x10000002, /**不支持的功能*/
BT_ERROR_TIMEOUT = 0x10000003, /**超时*/
BT_ERROR_DISCONNECTED = 0x10000004, /**连接断开*/
BT_ERROR_STATE = 0x10000005, /**当前状态不支持该函数调用*/
BT_ERROR_PARSING = 0x10000006, /**数据解析错误*/
BT_ERROR_POWER_OFF = 0x10000007, /**已关机*/
BT_ERROR_NOTIFY_CB_FULL = 0x10000008, /**注册回调已满*/
BT_ERROR_DEVICE_EXCEPTION = 0x10000009, /**设备故障*/
BT_ERROR_RESP_FAIL = 0x10000010, /**回复错误*/
BT_ERROR_AVRCP_NO_REG = 0x10000011, /**对端设备没有注册绝对音量*/
BT_ERROR_IN_PROGRESS = 0x10000012, /**有其他事件正在处理*/
BT_ERROR_OUT_OF_MEMORY = 0x10000013, /**没有内存*/
} bt_err_t;
//用户可调用以下接口断开pan连接,mac为蓝牙地址的数组形式,ext_profile需要传入BT_PROFILE_PAN
bt_err_t bt_interface_disc_ext(unsigned char *mac, bt_profile_t ext_profile);
//调用这个接口之前会把mac转换为BTS2S_BD_ADDR蓝牙地址格式
void bt_pan_disc(BTS2S_BD_ADDR *bd)
{
bts2_app_stru *bts2_app_data = getApp();
bts2_pan_inst_data *ptr = NULL;
ptr = bts2_app_data->pan_inst_ptr;
//状态必须是PAN_BUSY_ST才能断开
if (ptr->pan_st == PAN_BUSY_ST)
{
//断开pan
pan_disc_req(ptr->id);
USER_TRACE(">> PAN disconnect\n");
}
else
{
USER_TRACE(">> PAN disconnect fail");
}
}
pan事件处理:
pan连接状态回调event
pan连接成功:BT_NOTIFY_PAN_PROFILE_CONNECTED
pan连接失败:BT_NOTIFY_PAN_PROFILE_DISCONNECTED
备注
注意:两个接口传递的地址参数需要进行相应的转换。
// 调用连接pan的API之后,pan连接成功的消息通过notify给用户
// 用户需要实现接收notify event的hdl函数 如:bt_notify_handle
// BT_NOTIFY_PAN_PROFILE_CONNECTED event里面包含:地址信息 、profile_type、 res:0(成功)
// 断开pan后也会收到相应的事件
// BT_NOTIFY_PAN_PROFILE_DISCONNECTED event里面包含:地址信息 、profile_type、 原因
// 具体结构体信息参考API注释
static int bt_notify_handle(uint16_t type, uint16_t event_id, uint8_t *data, uint16_t data_len)
{
int ret = -1;
switch (type)
{
case BT_NOTIFY_PAN:
{
bt_sifli_notify_pan_event_hdl(event_id, data, data_len);
}
break;
}
return 0;
}
//profile断开和连接的通知事件
typedef struct
{
/// 对端设备地址
bt_notify_device_mac_t mac;
/// 连接或者断开的profie类型
uint8_t profile_type;
/// 连接或者断开结果
uint8_t res;
} bt_notify_profile_state_info_t;
static int bt_sifli_notify_pan_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
switch (event_id)
{
case BT_NOTIFY_PAN_PROFILE_CONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
//用户自己实现相应的处理函数
break;
}
case BT_NOTIFY_PAN_PROFILE_DISCONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
//用户自己实现相应的处理函数
break;
}
default:
return -1;
}
return 0;
}
pan基本功能使用
public API
//设置IP Address
void bt_pan_set_ip_addr(char *string);
//设置网络掩码
void bt_pan_set_netmask(char *string);
//设置网关
void bt_pan_set_gw(char *string);
//设置路由
void bt_pan_set_nap_route(char *string);
//设置DNS
void bt_pan_set_dns1(char *string);
void bt_pan_set_dns2(char *string);
//遍历当前的net device
void bt_pan_scan_proc_net_dev(void);
bt_lwip接口层
Sifli SDK的网络实现是基于lwip的,并且pan并不会直接调用lwip的接口,而是通过bt_lwip作为中间层间接进行调用。 LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。 LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。
//1.bt_lwip的初始化
static struct rt_bt_prot_ops ops =
{
//收到数据的回调
rt_bt_lwip_protocol_recv,
//register ETH device
rt_bt_lwip_protocol_register,
//unregister ETH device
rt_bt_lwip_protocol_unregister
};
int rt_bt_lwip_init(void)
{
static struct rt_bt_prot prot;
rt_bt_prot_event_t event;
rt_memset(&prot, 0, sizeof(prot));
rt_strncpy(&prot.name[0], RT_BT_PROT_LWIP, RT_BT_PROT_NAME_LEN);
prot.ops = &ops;
if (rt_bt_prot_regisetr(&prot) != RT_EOK)
{
LOG_E("F:%s L:%d protocol regisetr failed", __FUNCTION__, __LINE__);
return -1;
}
return 0;
}
//上电自动调用初始化
INIT_PREV_EXPORT(rt_bt_lwip_init);
rt_err_t rt_bt_prot_regisetr(struct rt_bt_prot *prot)
{
int i;
rt_uint32_t id;
static rt_uint8_t num;
/* Parameter checking */
if ((prot == RT_NULL) ||
(prot->ops->prot_recv == RT_NULL) ||
(prot->ops->dev_reg_callback == RT_NULL))
{
LOG_E("F:%s L:%d Parameter Wrongful", __FUNCTION__, __LINE__);
return -RT_EINVAL;
}
/* save prot */
bt_prot = prot;
return RT_EOK;
}
//再收到pan的连接事件BTS2MU_PAN_CONN_IND时,会把pan注册到bt_lwip里
void bt_lwip_pan_control_tcpip(bts2_app_stru *bts2_app_data)
{
bt_pan_instance[0].bts2_app_data = bts2_app_data;
bt_pan_instance[0].ops = &instance_ops;
rt_bt_prot_attach_pan_instance(&bt_pan_instance[0]);
}
rt_err_t rt_bt_prot_attach_pan_instance(struct rt_bt_pan_instance *panInstance)
{
panInstance->prot = bt_prot;
//会调用rt_bt_lwip_protocol_register
panInstance->prot = bt_prot->ops->dev_reg_callback(panInstance->prot, panInstance); /* attach prot */
return RT_EOK;
}
//2.pan发送数据的接口会注册到ETH device,从而将网络数据包通过pan发送出去
static rt_err_t rt_bt_lwip_protocol_send(rt_device_t device, struct pbuf *p)
{
struct rt_bt_pan_instance *bt_instance = ((struct eth_device *)device)->parent.user_data;
rt_uint8_t *frame;
/* sending data directly */
if (p->len == p->tot_len)
{
frame = (rt_uint8_t *)p->payload;
rt_bt_prot_transfer_instance(bt_instance, frame, p->tot_len);
LOG_D("F:%s L:%d run len:%d", __FUNCTION__, __LINE__, p->tot_len);
return RT_EOK;
}
frame = rt_malloc(p->tot_len);
if (frame == RT_NULL)
{
LOG_E("F:%s L:%d malloc out_buf fail\n", __FUNCTION__, __LINE__);
return -RT_ENOMEM;
}
/*copy pbuf -> data dat*/
pbuf_copy_partial(p, frame, p->tot_len, 0);
/* send data */
rt_bt_prot_transfer_instance(bt_instance, frame, p->tot_len);
LOG_D("F:%s L:%d run len:%d", __FUNCTION__, __LINE__, p->tot_len);
rt_free(frame);
return RT_EOK;
}
rt_err_t rt_bt_prot_transfer_instance(struct rt_bt_pan_instance *bt_instance, void *buff, int len)
{
if (bt_instance->ops->bt_send != RT_NULL)
{
//调用bt_lwip_pan_send
bt_instance->ops->bt_send(bt_instance, buff, len);
return RT_EOK;
}
return -RT_ERROR;
}
//pan发送数据
void bt_lwip_pan_send(struct rt_bt_pan_instance *bt_instance, void *buff, int len)
{
bts2_pan_inst_data *ptr = NULL;
void *p;
U8 *eth_header;
ptr = bt_instance->bts2_app_data->pan_inst_ptr;
if (ptr->pan_st == PAN_BUSY_ST)
{
//pan发送数据会退出sniff,以达到更快的性能
if (ptr->mode == SNIFF_MODE)
bt_exit_sniff_mode(bt_instance->bts2_app_data);
BTS2S_PAN_DATA_REQ *msg;
msg = (BTS2S_PAN_DATA_REQ *)bmalloc(sizeof(BTS2S_PAN_DATA_REQ));
BT_OOM_ASSERT(msg);
if (msg)
{
msg->type = BTS2MD_PAN_DATA_REQ;
eth_header = buff;
msg->ether_type = (eth_header[12] << 8) + eth_header[13];
msg->len = len - 14;
buff = eth_header + 14;
msg->dst_addr.w[0] = (((U16)eth_header[0]) << 8) | (U16)eth_header[1];
msg->dst_addr.w[1] = (((U16)eth_header[2]) << 8) | (U16)eth_header[3];
msg->dst_addr.w[2] = (((U16)eth_header[4]) << 8) | (U16)eth_header[5];
msg->src_addr = bt_pan_get_mac_address(bt_instance);
p = bmalloc(msg->len);
BT_OOM_ASSERT(p);
if (p == NULL)
{
bfree(msg);
return;
}
memcpy(p, buff, msg->len);
msg->payload = p;
bts2_msg_put(bts2_task_get_pan_task_id(), BTS2M_PAN, msg);
}
}
else
{
USER_TRACE(">> PAN data send fail\n");
}
}
//3.pan接收到网络数据包后,会调用注册在pan的接收数据回调
case BTS2MU_PAN_DATA_IND:
{
BTS2S_PAN_DATA_IND *msg;
msg = (BTS2S_PAN_DATA_IND *)bts2_app_data->recv_msg;
msg->len = msg->len + 14;
memcpy(msg->payload, msg->dst_addr.w, 6);
memcpy(msg->payload + 6, msg->src_addr.w, 6);
msg->payload[12] = (msg->ether_type >> 8);
msg->payload[13] = (msg->ether_type & 0xff);
if (0x86dd == msg->ether_type)
{
bfree(msg->payload);
break;
}
//调用相应的回调
rt_bt_instance_transfer_prot(&bt_pan_instance[0], (void *)msg->payload, msg->len);
bfree(msg->payload);
break;
}
//通过rt_bt_prot_regisetr将prot注册到bt_prot,再通过rt_bt_prot_attach_pan_instance将bt_prot注册到pan
rt_err_t rt_bt_instance_transfer_prot(struct rt_bt_pan_instance *bt_instance, void *buff, int len)
{
struct rt_bt_prot *prot = bt_instance->prot;
if (prot != RT_NULL)
{
return prot->ops->prot_recv(bt_instance, buff, len);
}
return -RT_ERROR;
}
PAN功能使用demo
首先在工程初始化时,注册接收notify event 的处理函数
输入需要连接手机的mac地址,等待连接成功的消息
想要使用pan,必须要在手机端打开蓝牙网络共享功能,不同操作系统的手机蓝牙网络共享功能开启方式可在网上查询
//register notify event handle function start
int bt_sifli_notify_pan_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
switch (event_id)
{
case BT_NOTIFY_PAN_PROFILE_CONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
//用户自己实现相应的处理函数
break;
}
case BT_NOTIFY_PAN_PROFILE_DISCONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
//用户自己实现相应的处理函数
break;
}
default:
return -1;
}
return 0;
}
static int bt_sifli_notify_common_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
switch (event_id)
{
//bt功能开启成功,可以正常使用
case BT_NOTIFY_COMMON_BT_STACK_READY:
{
break;
}
//bt关闭成功
case BT_NOTIFY_COMMON_CLOSE_COMPLETE:
{
break;
}
// ACL 连接成功
case BT_NOTIFY_COMMON_ACL_CONNECTED:
{
bt_notify_device_acl_conn_info_t *acl_info = (bt_notify_device_acl_conn_info_t *) data;
//device acl connected
break;
}
// ACL 断开连接成功
case BT_NOTIFY_COMMON_ACL_DISCONNECTED:
{
bt_notify_device_base_info_t *device_info = (bt_notify_device_base_info_t *)data;
//device acl disconnected
break;
}
default:
return -1;
}
return 0;
}
static int bt_notify_handle(uint16_t type, uint16_t event_id, uint8_t *data, uint16_t data_len)
{
int ret = -1;
switch (type)
{
case BT_NOTIFY_COMMON:
{
ret = bt_sifli_notify_common_event_hdl(event_id, data, data_len);
}
break;
case BT_NOTIFY_PAN:
{
bt_sifli_notify_pan_event_hdl(event_id, data, data_len);
}
break;
default:
break;
}
return 0;
}
int app_bt_notify_init(void)
{
bt_interface_register_bt_event_notify_callback(bt_notify_handle);
return BT_EOK;
}
INIT_ENV_EXPORT(app_bt_notify_init);
//register notify event handle function end
// start connect with phone:001bdcf4b6bd
unsigned char mac[6] = {0x00,0x1b,0xdc,0xf4,0xb6,0xbd}
bt_interface_conn_ext((unsigned char *)(mac), BT_PROFILE_PAN);
//