Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9463532
  • 博文数量: 1750
  • 博客积分: 12961
  • 博客等级: 上将
  • 技术积分: 20091
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-09 11:25
个人简介

偷得浮生半桶水(半日闲), 好记性不如抄下来(烂笔头). 信息爆炸的时代, 学习是一项持续的工作.

文章分类

全部博文(1750)

文章存档

2024年(26)

2023年(26)

2022年(112)

2021年(217)

2020年(157)

2019年(192)

2018年(81)

2017年(78)

2016年(70)

2015年(52)

2014年(40)

2013年(51)

2012年(85)

2011年(45)

2010年(231)

2009年(287)

分类: Android平台

2016-05-17 12:36:16

http://blog.csdn.net/eastmoon502136/article/details/8725877

这里主要摘取对于hci,l2cap,sdp和rfcomm的一些应用编程。

 

关于hci

 

一、HCI层协议概述

 

1、HCI Command Packets


详见bluez源码:lib/hci.h

[html] view plain copy
  1. /* Link Control */  
  2. #define OGF_LINK_CTL     0x01  
  3. #define OCF_INQUIRY         0x0001  
  4. #define OCF_INQUIRY_CANCEL      0x0002  
  5. #define OCF_PERIODIC_INQUIRY        0x0003  
  6. #defineOCF_EXIT_PERIODIC_INQUIRY   0x0004  
  7. #define OCF_CREATE_CONN         0x0005  
  8. #define OCF_DISCONNECT          0x0006  
  9. #define OCF_ADD_SCO            0x0007  
  10. ……  
  11.    
  12. /* Link Policy */  
  13. #define OGF_LINK_POLICY     0x02  
  14. #define OCF_HOLD_MODE           0x0001  
  15. #define OCF_SNIFF_MODE          0x0003  
  16. #define OCF_EXIT_SNIFF_MODE 0x0004  
  17. #define OCF_PARK_MODE           0x0005  
  18. #define OCF_EXIT_PARK_MODE  0x0006  
  19. #define OCF_QOS_SETUP           0x0007  
  20. #define OCF_ROLE_DISCOVERY  0x0009  
  21. #define OCF_SWITCH_ROLE     0x000B  
  22. #define OCF_READ_LINK_POLICY        0x000C  
  23. #defineOCF_WRITE_LINK_POLICY       0x000D  
  24. #defineOCF_READ_DEFAULT_LINK_POLICY    0x000E  
  25. #defineOCF_WRITE_DEFAULT_LINK_POLICY   0x000F  
  26. #defineOCF_FLOW_SPECIFICATION          0x0010  
  27. #define OCF_SNIFF_SUBRATING            0x0011  
  28.    
  29. /* Host Controller andBaseband */  
  30. #define OGF_HOST_CTL     0x03  
  31. #define OCF_SET_EVENT_MASK      0x0001  
  32. #define OCF_RESET           0x0003  
  33. #define OCF_SET_EVENT_FLT       0x0005  
  34. #define OCF_FLUSH           0x0008  
  35. #define OCF_READ_PIN_TYPE       0x0009  
  36. #define OCF_WRITE_PIN_TYPE      0x000A  
  37. #defineOCF_CREATE_NEW_UNIT_KEY     0x000B  
  38. #defineOCF_READ_STORED_LINK_KEY    0x000D  
  39. #defineOCF_WRITE_STORED_LINK_KEY   0x0011  
  40. #defineOCF_DELETE_STORED_LINK_KEY  0x0012  
  41. #defineOCF_CHANGE_LOCAL_NAME       0x0013  
  42. #define OCF_READ_LOCAL_NAME        0x0014  
  43. #defineOCF_READ_CONN_ACCEPT_TIMEOUT    0x0015  
  44. #defineOCF_WRITE_CONN_ACCEPT_TIMEOUT   0x0016  
  45. #defineOCF_READ_PAGE_TIMEOUT       0x0017  
  46. ……  
  47.    
  48. /* InformationalParameters */  
  49. #define OGF_INFO_PARAM      0x04  
  50. #defineOCF_READ_LOCAL_VERSION          0x0001  
  51. #defineOCF_READ_LOCAL_COMMANDS     0x0002  
  52. #defineOCF_READ_LOCAL_FEATURES         0x0003  
  53. #defineOCF_READ_LOCAL_EXT_FEATURES 0x0004  
  54. #define OCF_READ_BUFFER_SIZE                0x0005  
  55. #defineOCF_READ_COUNTRY_CODE          0x0007  
  56. #define OCF_READ_BD_ADDR                   0x0009  
  57. #define OCF_READ_DATA_BLOCK_SIZE      0x000A  
  58.    
  59. /* Status params */  
  60. #define OGF_STATUS_PARAM 0x05  
  61. #defineOCF_READ_FAILED_CONTACT_COUNTER     0x0001  
  62. #defineOCF_RESET_FAILED_CONTACT_COUNTER    0x0002  
  63. #defineOCF_READ_LINK_QUALITY       0x0003  
  64. #define OCF_READ_RSSI           0x0005  
  65. #define OCF_READ_AFH_MAP        0x0006  
  66. #define OCF_READ_CLOCK          0x0007  
  67. #defineOCF_READ_LOCAL_AMP_INFO 0x0009  
  68. #defineOCF_READ_LOCAL_AMP_ASSOC    0x000A  
  69. #defineOCF_WRITE_REMOTE_AMP_ASSOC  0x000B  


2、HCI ACL Data Packets


 

 

3、HCI synchronous (SCO and eSCO) DataPackets


 

 

4、HCI Event Packet


 

[html] view plain copy
  1. #define EVT_INQUIRY_COMPLETE        0x01  
  2. #define EVT_INQUIRY_RESULT      0x02  
  3. #define EVT_CONN_COMPLETE       0x03  
  4. #define EVT_CONN_REQUEST           0x04  
  5. #define EVT_DISCONN_COMPLETE        0x05  
  6. #define EVT_AUTH_COMPLETE       0x06  
  7. #defineEVT_REMOTE_NAME_REQ_COMPLETE    0x07  
  8. #define EVT_ENCRYPT_CHANGE      0x08  
  9. ……  

 

HCI Event分3种:Command complete Event,Command States Event,Command Subsequently Completed.

1)、Command complete Event: 如果Host发送的Command可以立刻有结果,则会发送此类

     Event。也就是说,如果发送的Command只与本地Modules有关,不与remote设备打  

     交道,则使用Command complete Event。例如:HCI_Read_Buffer_Size.

2)、Command States Event:如果Host发送的Command不能立刻得知结果,则发送此类   

     Event。Host发送的Command执行要与Remote设备打交道,则必然无法立刻得知结

     果,所以会发送Command States Event.例如:

 HCI Connect。

3)、Command SubsequentlyCompleted:Command延后完成Event。例如:连接已建立。

 

 

例子:remote namerequest

 

从这里可以看出,如果Host发送的Command是与Remote device有关的,则会先发送Command States Event 。等动作真正完成了,再发送Command Subsequently Completed。

 

l2cap数据传输

 

 


 

一个l2cap包会按照规则先切割为多个HCI数据包。HCI数据包再通过HCI-usb这一层传递给USB设备。每个包又通过USB driver发送到底层。

 

二、HCI层的编程

HCI是沟通上层协议以及程序与底层硬件协议的通道。所以,通过HCI发送的Command都是上层协议或者应用程序发送给Bluetooth device的。它命令Bluetooth device(或其中的硬件协议)去做什么何种动作。

Socket基础概念:

使用函数socket()建立一个Socket,就如同你有一部电话。bind()则是把这个电话和某个电话号码(网络地址)对应起来。

类似的,我们可以把Host理解为一个房间,这个房间有多部电话(Dongle)。

当使用socket() 打开一个HCI protocol的socket,表明得到这个房间的句柄。HOST可能会有多个device。换句话说,这个房间可以有多个电话号码。所以HCI会提供一套指令去得到这些device。

 

得到Host上插入设备数目以及device信息

[html] view plain copy
  1. // 0. 分配一个空间给 hci_dev_list_req。这里面将放所有device信息。  
  2. structhci_dev_list_req *dl;  
  3. structhci_dev_req *dr;  
  4. structhci_dev_info di;  
  5. int i;  
  6.    
  7.  if (!(dl = malloc(HCI_MAX_DEV * sizeof(structhci_dev_req) + sizeof(uint16_t)))) {  
  8.   perror("Can't allocate memory");  
  9.   exit(1);  
  10.  }  
  11.  dl->dev_num = HCI_MAX_DEV;  
  12.  dr = dl->dev_req;  

[html] view plain copy
  1. //1. 打开一个HCI socket.此socket相当于一个房间。  
  2. if ((ctlsocket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {  
  3.   perror("Can't open HCI socket.");  
  4.   exit(1);  
  5.  }  
  6.    

[html] view plain copy
  1. // 2. 使用HCIGETDEVLIST,得到所有device的Device ID。存放在dl中。    
  2.  if (ioctl(ctl, HCIGETDEVLIST, (void *) dl)< 0) {  
  3.   perror("Can't get device list");  
  4.   exit(1);  
  5.  }  

 

[html] view plain copy
  1. // 2. 使用HCIGETDEVLIST,得到所有device的Device ID。存放在dl中。    
  2.  if (ioctl(ctl, HCIGETDEVLIST, (void *) dl)< 0) {  
  3.   perror("Can't get device list");  
  4.   exit(1);  
  5.  }  

device信息:

[html] view plain copy
  1. struct hci_dev_info {  
  2.  uint16_t dev_id;   //Device ID  
  3.  char    name[8];  //device name  
  4.  bdaddr_t bdaddr;   //device bdaddr  
  5.  uint32_t flags;    //device Flags:如:UP,RUNING,Down等。  
  6.  uint8_t type;   //device 连接方式:如USB,PC Card,UART,RS232等。  
  7.  uint8_t features[8];  
  8.  uint32_t pkt_type;  
  9.  uint32_t link_policy;  
  10.  uint32_t link_mode;  
  11.  uint16_t acl_mtu;  
  12.  uint16_t acl_pkts;  
  13.  uint16_t sco_mtu;  
  14.  uint16_t sco_pkts;  
  15.  struct  hci_dev_stats stat;  //此device的数据信息,如发送多少个ACL Packet,正确多少,错误多少,等等。  
  16. };  


UP和Down Bluetooth device:

[html] view plain copy
  1. ioctl(ctl, HCIDEVUP, hdev)  
  2. ioctl(ctl, HCIDEVDOWN, hdev)  
  3. ctl:为使用socket(AF_BLUETOOTH,SOCK_RAW, BTPROTO_HCI)打开的Socket.  
  4. hdev: Device ID.(所以上面的Socket不需要bind,因为这边指定了)  


 BlueZ提供的HCI编程接口一(针对本地device的API系列)

 

1 打开一个HCI Socket

inthci_open_dev(int dev_id):

这个function用来打开一个HCI Socket。它首先打开一个HCI protocol的Socket(房间),并将此Socket与device ID=参数dev_id的device绑定起来。只有bind后,它才将Socket句柄与device对应起来。

注意,所有的HCI Command发送之前,都需要使用 hci_open_dev打开并绑定。

 

2 关闭一个HCI Socket:

inthci_close_dev(int dd) //简单的关闭使用hci_open_dev打开的Socket。

 

3  向HCI Socket(对应一个Dongle)发送 request

inthci_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 最为重要,首先看它的结构:

[html] view plain copy
  1. structhci_request {  
  2.     uint16_togf;    //Opcode Group  
  3.     uint16_tocf;    //Opcode Command  
  4.     int      event; //此Command产生的Event类型。  
  5.     void     *cparam; //Command 参数  
  6.     int      clen;   //Command参数长度  
  7.  void    *rparam;  //Response 参数  
  8.     int      rlen;   //Response 参数长度  
  9. };  

ogf,ocf不用多说,对应前面的图就明白这是Group Code和Command Code。这两项先确定下来,然后可以查HCI Spec。察看输入参数(cparam)以及输出参数(rparam)含义。至于他们的结构以及参数长度,则在~/include/net/bluetooth/hci.h中有定义。

至于event.如果设置,它会被setsockopt设置于Socket。

 

例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。

可以使用ioctlHCIGETCONNINFO得到ACL 连接Handle。

[html] view plain copy
  1. ioctl(dd, HCIGETCONNINFO,(unsigned long) cr);  
  2. Connect_handle =htobs(cr->conn_info->handle);  

 

所以完整的过程如下:

 

[html] view plain copy
  1. struct hci_requestHCI_Request;  
  2.  read_link_policy_cp Command_Param;  
  3.  read_link_policy_rp Response_Param;  
  4. // 1.得到ACL Connect Handle  
  5. if (ioctl(dd,HCIGETCONNINFO, (unsigned long) cr) < 0)  
  6.  {   
  7.   return -1;  
  8.  }  
  9.  Connect_handle = htobs(cr->conn_info->handle);  
  10.    
  11. memset(&HCI_Request, 0,sizeof(HCI_Request));  
  12.  memset(&Command_Param, 0 ,sizeof(Command_Param));  
  13.  memset(&Response_Param, 0 ,sizeof(Response_Param));  

 

[html] view plain copy
  1. // 2.填写Command输入参数  
  2.  Command_Param.handle = Connect_handle;  
  3.    
  4.    
  5.  HCI_Request.ogf = OGF_LINK_POLICY;  //Command组ID  
  6.  HCI_Request.ocf = OCF_READ_LINK_POLICY;//Command ID  
  7.  HCI_Request.cparam = &Command_Param;  
  8.  HCI_Request.clen = READ_LINK_POLICY_CP_SIZE;  
  9.  HCI_Request.rparam = &Response_Param;  
  10.  HCI_Request.rlen = READ_LINK_POLICY_RP_SIZE;  
  11.    
  12. if (hci_send_req(dd,&HCI_Request, to) < 0)  
  13.  {  
  14.   perror("\nhci_send_req()");  
  15.   return -1;  
  16.  }  
  17. //如果返回值状态不对  
  18.  if (Response_Param.status) {  
  19.   return -1;  
  20.  }  
  21.    
  22. //得到当前policy  
  23.  *policy = Response_Param.policy;<span style="font-size:18px; "> span>  

[html] view plain copy
  1. 4 几个更基础的function  
  2. static inline void bacpy(bdaddr_t*dst, const bdaddr_t *src) //bdaddr copy  
  3. static inline intbacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)//bdaddr 比较  


[html] view plain copy
  1. 5 得到指定Dongle BDAddr  
  2. int hci_read_bd_addr(int dd,bdaddr_t *bdaddr, int to);  
  3. 参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。  
  4. 参数2:输出参数,其中会放置bdaddr.  
  5. 参数3:以milliseconds为单位的timeout.  

 

[html] view plain copy
  1. 6 读写device Name  
  2. int hci_read_local_name(intdd, int len, char *name, int to)  
  3. int hci_write_local_name(intdd, const char *name, int to)  
  4. 参数1:HCI Socket,使用hci_open_dev()打开的Socket(Dongle)。  
  5. 参数2:读取或设置Name。  
  6. 参数3:以milliseconds为单位的timeout.  
  7. 注意:这里的Name与IOCTL HCIGETDEVINFO 得到hci_dev_info中的name不同。  

[html] view plain copy
  1. 7 得到HCI Version:  
  2. inthci_read_local_version(int dd, struct hci_version *ver, int to)  


[html] view plain copy
  1. 8 得到已经UP的device BDaddr  
  2. int hci_devba(int dev_id,bdaddr_t *bdaddr);  
  3. dev_id: Device ID.  
  4. bdaddr:输出参数,指定device如果UP, 则放置其BDAddr。  


[html] view plain copy
  1. 9 得到device Info  
  2. int hci_devinfo(int dev_id,struct hci_dev_info *di)  
  3. dev_id:  Device ID.  
  4. di: 此device信息。  
  5. 出错返回 -1。  
  6. 注意,这个Function的做法与3.0的方法完全一致。  


[html] view plain copy
  1. 10 从hciX中得到X  
  2. int hci_devid(const char*str)  
  3. str: 类似 hci0这样的字串。  
  4. 如果hciX对应的Device ID(X)是现实存在且UP。则返回此设备Device ID。  


[html] view plain copy
  1. 11得到BDADDR不等于参数bdaddr的Device ID  
  2. int hci_get_route(bdaddr_t*bdaddr)  
  3. 查找device,发现device Bdaddr不等于参数bdaddr的第一个device,则返回此 Device ID。所以,如果: int hci_get_route(NULL),则得到第一个可用的 Device ID。  

[html] view plain copy
  1. 12 将BDADDR转换为字符串  
  2. int ba2str(const bdaddr_t*ba, char *str)  


[html] view plain copy
  1. 13  将自串转换为BDADDR  
  2. int str2ba(const char *str,bdaddr_t *ba)  

 

BlueZ提供的HCI编程接口二(针对Remote Device的API系列)

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(intdd, 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的。

 

例子:

//scan是一个利用hci层协议 获得远程蓝牙设备的蓝牙地址和蓝牙昵称的函数;

 

[html] view plain copy
  1. int scan()  
  2. {  
  3.     inquiry_info *ii = NULL;  
  4.     int max_rsp, num_rsp;  
  5.     int dev_id, sock, len, flags;  
  6.     int i;  
  7.     char addr[19] = { 0 };  
  8.     char name[248] = { 0 };  
  9.      
  10.     dev_id = hci_get_route(NULL);  
  11.     printf("/nhci%d is scanning....../n",dev_id);  
  12.     //dev_id = lc[scannum].name;  
  13.     sock = hci_open_dev( dev_id );  
  14.     if (dev_id < 0 || sock < 0)  
  15.     {  
  16.         system("reboot");  
  17.         perror("opening socket");  
  18.         return 0;  
  19.     }  
  20.     bdaddr_t src;  
  21.     bacpy(&src, BDADDR_ANY);  
  22.      
  23.     len  = 8;  
  24.     max_rsp = 255;  
  25.     flags = IREQ_CACHE_FLUSH;  
  26.     ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));  
  27.      
  28.     num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii,flags);  
  29.     if( num_rsp < 0 ) perror("hci_inquiry");  
  30.    
  31.     for (i = 0; i < num_rsp; i++)  
  32.     {  
  33.         ba2str(&(ii+i)->bdaddr, addr);  
  34.         memset(name, 0, sizeof(name));  
  35.         int c=0;  
  36.         if (sdp_get_channel_opush(&src, &(ii+i)->bdaddr,&c))  
  37.         //printf("/n%d/n",c);  
  38.         if(c>0)addadr(addr,c);    //此处是我在做项目中做的一个与应用程序连接的接口函数,功能是完成远程蓝牙设备的地址和文件传输通道号添加到我自己建的地址列表中.  
  39.         //printf("chinal=%d/n",c);  
  40.          
  41.         // if (hci_read_remote_name(sock, &(ii+i)->bdaddr,sizeof(name), name, 0) < 0)  
  42.         // strcpy(name, "[unknown]");  
  43.         // printf("%s %s/n", addr, name);  
  44.     }  
  45.    
  46.     free( ii );  
  47.      
  48.     close( sock );  
  49.    
  50.     return 0;  
  51. }  

 

//sdp_get_channel_opush,这是一个我自己编写的通过 hci获得的远程蓝牙地址和初始化为零的channel来获取远程蓝牙设备有无文件传输功能,并获得文件传输通道的过程。

 

[html] view plain copy
  1. intsdp_get_channel_opush(bdaddr_t *src, bdaddr_t *dst, int *channel)  
  2. {  
  3.     uuid_t service;  
  4.     sdp_session_t *session;  
  5.     sdp_list_t *search, *attrs, *rsp;  
  6.     uint16_t attr;  
  7.     int err;  
  8.     /* build search request */  
  9.     sdp_uuid16_create(&service, OBEX_OBJPUSH_SVCLASS_ID);  
  10.     search = sdp_list_append(0, &service);  
  11.     attr = SDP_ATTR_PROTO_DESC_LIST;  
  12.     attrs = sdp_list_append(NULL, &attr);  
  13.      
  14.     /* connect */  
  15.     session = sdp_connect(src, dst, SDP_RETRY_IF_BUSY);  
  16.     if (!session) return(-1);  
  17.     /* send request */  
  18.     err = sdp_service_search_attr_req(session, search,SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);  
  19.     /* close connection */  
  20.     sdp_close(session);  
  21.     printf("sdp_service_search_attr_req-return=%d/n",err);  
  22.     if (err) return(0);  
  23.      
  24.     /* get rfcomm channel */  
  25.     for(; rsp; rsp = rsp->next)  
  26.     {  
  27.         sdp_record_t *rec = (sdp_record_t *) rsp->data;  
  28.         sdp_list_t *protos;  
  29.      
  30.         if (!sdp_get_access_protos(rec, &protos))  
  31.         {  
  32.             int ch = sdp_get_proto_port(protos, RFCOMM_UUID);  
  33.             printf("channel=%d/n",ch);  
  34.             if (ch > 0)  
  35.             {  
  36.                 *channel = ch;  
  37.                 return(1);  
  38.             }  
  39.         }  
  40.     }  
  41.    
  42.     return(0);  
  43. }  


关于l2cap

 

一、L2CAP协议简介

L2CAP 基于 通道(channel) 的概念。 通道 (Channel) 是位于基带 (baseband) 连接之上的逻辑连接。每个通道以多对一的方式绑定一个单一协议(single protocol)。多个通道可以绑定同一个协议,但一个通道不可以绑定多个协议。每个在通道里接收到的 L2CAP 数据包被传到相应的上层协议。 多个通道可共享同一个基带连接。

也就是说,所有L2CAP数据均通过HCI传输到Remote Device。且上层协议的数据,大都也通过L2CAP来传送。

 

L2CAP可以发送Command。例如连接,断连等等。其命令格式如下

Command format:


 

 

 

例子:CONNECTIONREQUEST (CODE 0x02)



Identifier:请求的匹配响应。

Source CID:Set up sourceconnection channel identifier

PSM(Protocol/ServiceMultiplexer):比较需要注意,L2CAP 使用L2CAP连接请求(ConnectionRequest )命令中的PSM字段实现协议复用。L2CAP可以复用发给上层协议的连接请求,这些上层协议包括服务发现协议SDP(PSM = 0x0001)、RFCOMM(PSM = 0x0003)和电话控制(PSM = 0x0005)等。

 

Protocol

PSM

SDP

0x0001

RFCOMM

0x0003

TCS-BIN

0x0005

TCS-BIN-CORDLESS

0x0007

BNEP

0x000F

HID_Control

0x0011

HID_Interrupt

0x0013

UPnP

0x0015

AVCTP

0x0017

AVDTP

0x0019

AVCTP_Browsing

0x001B

UDI_C-Plane

0x001D

 

 

 

二、L2CAP编程方法

几乎所有协议的连接,断连,读写都是用L2CAP连接来做的。

 

1.创建L2CAP Socket:

[html] view plain copy
  1. socket(PF_BLUETOOTH,SOCK_RAW, BTPROTO_L2CAP);  


domain=PF_BLUETOOTH,type可以是多种类型。protocol=BTPROTO_L2CAP.

 

2.绑定:

[html] view plain copy
  1. // Bindto local address  
  2.  memset(&addr, 0, sizeof(addr));  
  3.  addr.l2_family = AF_BLUETOOTH;  
  4.  bacpy(&addr.l2_bdaddr, &bdaddr);//bdaddr为本地Dongle BDAddr  
  5.  if (bind(sk, (struct sockaddr *) &addr,sizeof(addr)) < 0) {  
  6.   perror("Can't bind socket");  
  7.   goto error;  
  8.  }  

 

3.连接

[html] view plain copy
  1. memset(&addr,0, sizeof(addr));  
  2. addr.l2_familyAF_BLUETOOTH;  
  3. bacpy(addr.l2_bdaddr,src);  
  4. addr.l2_psmxxx;  
  5.  if (connect(sk, (struct sockaddr *) &addr,sizeof(addr)) < 0) {  
  6.   perror("Can't connect");  
  7.   goto error;  
  8.  }  

 

注意:

[html] view plain copy
  1. memset(&addr,0, sizeof(addr));  
  2. addr.l2_familyAF_BLUETOOTH;  
  3. bacpy(addr.l2_bdaddr,src);  
  4. addr.l2_psmxxx;  
  5.  if (connect(sk, (struct sockaddr *) &addr,sizeof(addr)) < 0) {  
  6.   perror("Can't connect");  
  7.   goto error;  
  8.  }  

 

4. 发送数据到Remote Device:

send()或write()都可以。

 

5. 接收数据:

revc() 或read()

 

Bluetooth设备的状态

之前HCI编程时,是用 ioctl(HCIGETDEVINFO)得到某个Device Info(hci_dev_info).其中flags当时解释的很简单。其实它存放着BluetoothDevice(例如:USB BluetoothDongle)的当前状态:

其中,UP,Down状态表示此Device是否启动起来。可以使用ioctl(HCIDEVUP)等修改这些状态。

另外:就是Inquiry Scan,PAGE Scan这些状态:

Sam在刚开始自己做L2CAP层连接时,使用另一台Linux机器插USB BluetoothDongle作Remote Device。怎么也没法使用inquiry扫描到remote设备,也没法连接remote设备,甚至无法使用l2ping ping到remote设备。觉得非常奇怪,后来才发现Remote Device状态设置有问题。没有设置PSCAN和ISCAN。

InquiryScan状态表示设备可被inquiry. PageScan状态表示设备可被连接。

#hciconfighci0 iscan

#hciconfighci0 pscan

或者:#hciconfighci0 piscan

就可以设置为PSCAN或者iSCAN状态了。

编程则可以使用ioctl(HCISETSCAN). dev_opt = SCAN_INQUIRY;dr.dev_opt = SCAN_PAGE;dr.dev_opt = SCAN_PAGE |SCAN_INQUIRY;

则可以inquiry或者connect了。

 

例一:发送SignalingPacket:

SignalingCommand是2个Bluetooth实体之间的L2CAP层命令传输。所以得SignalingCommand使用CID 0x0001.

多个Command可以在一个C-frame(control frame)中发送。

 


如果要直接发送SignalingCommand.需要建立SOCK_RAW类型的L2CAP连接Socket。这样才有机会自己填充Command Code,Identifier等。

 

发送signalingCommand以及接收Response的简单例子:

[html] view plain copy
  1. #include<stdio.h>  
  2. #include<sys/types.h>           
  3. #include<sys/socket.h>  
  4. #include<stdlib.h>  
  5. #include<poll.h>  
  6.    
  7. #include<bluetooth/bluetooth.h>  
  8. #include<bluetooth/hci.h>  
  9. #include<bluetooth/hci_lib.h>  
  10. #include<bluetooth/l2cap.h>  
  11.    
  12. intmain(int argc,  char** argv)  
  13. {  
  14.     int l2_sck = 0;  
  15.     int iRel = 0;  
  16.     struct sockaddr_l2 local_l2_addr;  
  17.     struct sockaddr_l2 remote_l2_addr;  
  18.     char str[24] ={0};  
  19.     int len = 0;  
  20.     int size = 50;  
  21.     char* send_buf;  
  22.     char* recv_buf;  
  23.     int i = 0;  
  24.     int id = 1; //不要为0  
  25.      
  26.     send_buf = malloc(L2CAP_CMD_HDR_SIZE +size);  
  27.     recv_buf = malloc(L2CAP_CMD_HDR_SIZE +size);  
  28.      
  29.     if(argc < 2)  
  30.     {  
  31.         printf("\n%s<bdaddr>\n", argv[0]);  
  32.         exit(0);  
  33.     }  
  34.      
  35.     // create l2cap raw socket  
  36.     l2_sck = socket(PF_BLUETOOTH, SOCK_RAW,BTPROTO_L2CAP); //创建L2CAPprotocol的RAW Packet  
  37.     if(l2_sck < 0)  
  38.     {  
  39.         perror("\nsocket:");  
  40.         eturn -1;  
  41.     }  
  42.      
  43.     //bind  
  44.     memset(&local_l2_addr, 0, sizeof(structsockaddr_l2));  
  45.     local_l2_addr.l2_family = PF_BLUETOOTH;  
  46.     bacpy(&local_l2_addr.l2_bdaddr ,BDADDR_ANY);  
  47.     iRel = bind(l2_sck, (struct sockaddr*)&local_l2_addr, sizeof(struct sockaddr_l2));  
  48.     if(iRel < 0)  
  49.     {  
  50.         perror("\nbind()");  
  51.         exit(0);  
  52.     }  
  53.      
  54.     //connect  
  55.     memset(&remote_l2_addr, 0 ,sizeof(struct sockaddr_l2));  
  56.     remote_l2_addr.l2_family = PF_BLUETOOTH;  
  57.     //printf("\nConnect to %s\n",argv[1]);  
  58.     str2ba(argv[1],&remote_l2_addr.l2_bdaddr);  
  59.     iRel = connect(l2_sck, (structsockaddr*)&remote_l2_addr, sizeof(struct sockaddr_l2));  
  60.     if(iRel < 0)  
  61.     {  
  62.         perror("\nconnect()");  
  63.         exit(0);  
  64.     }  
  65.      
  66.     //get local bdaddr  
  67.     len = sizeof(struct sockaddr_l2);  
  68.     memset(&local_l2_addr, 0, sizeof(structsockaddr_l2));  
  69.     //注意,getsockname()参数三是一个输入输出参数。输入时,为参数二的总体长度。输出时,  
  70.     //为实际长度。  
  71.     iRel = getsockname(l2_sck, (structsockaddr*) &local_l2_addr, &len);  
  72.     if(iRel < 0)  
  73.     {  
  74.         perror("\ngetsockname()");  
  75.         exit(0);  
  76.     }  
  77.     ba2str(&(local_l2_addr.l2_bdaddr), str);  
  78.     //printf("\nLocal Socketbdaddr:[%s]\n", str);  
  79.     printf("l2ping: [%s] from [%s](datasize %d) ...\n", argv[1], str, size);  
  80.     for (i = 0; i < size; i++)  
  81.         send_buf[L2CAP_CMD_HDR_SIZE + i] = 'A';  
  82.     l2cap_cmd_hdr *send_cmd = (l2cap_cmd_hdr *)send_buf;  
  83.     l2cap_cmd_hdr *recv_cmd = (l2cap_cmd_hdr *)recv_buf;  
  84.     send_cmd->ident = id;  //如上图所示,这一项为此CommandIdentifier  
  85.     send_cmd->len   = htobs(size);  
  86.     send_cmd->code = L2CAP_ECHO_REQ;  //如上图所示,此项为Command code.这项定为:  
  87.     //Echo Request。对端会发送Response回来。code=L2CAP_ECHO_RSP  
  88.      
  89.     while(1)  
  90.     {  
  91.         send_cmd->ident = id;  
  92.         if(send(l2_sck, send_buf, size +L2CAP_CMD_HDR_SIZE, 0) <= 0)  
  93.         {  
  94.             perror("\nsend():");  
  95.         }  
  96.      
  97.         while(1)  
  98.         {  
  99.             if(recv(l2_sck, recv_buf, size +L2CAP_CMD_HDR_SIZE, 0) <= 0)  
  100.             {  
  101.                 perror("\nrecv()");  
  102.             }  
  103.      
  104.             if (recv_cmd->ident != id)  
  105.                 continue;  
  106.             if( recv_cmd->code ==L2CAP_ECHO_RSP)  
  107.             {  
  108.                 //printf("\nReceiveResponse Packet.\n");  
  109.                 printf("%d bytes from [%s]id %d\n", recv_cmd->len, argv[1], recv_cmd->ident);  
  110.                 break;  
  111.             }  
  112.      
  113.         }  
  114.         sleep(1);  
  115.         id ++;   
  116.     }  
  117.      
  118.     close(l2_sck);  
  119.    
  120.     return 0;  
  121. }  

 

所以说,如果想要发送接收signalingCommand。只需要建立l2cap RAWsocket. 并按规则填充command id,command code等。就可以接收发送了。

 

CommandCode: 这个值放在l2cap.h中。

 

[html] view plain copy
  1. #defineL2CAP_COMMAND_REJ 0x01  
  2. #defineL2CAP_CONN_REQ  0x02  
  3. #defineL2CAP_CONN_RSP  0x03  
  4. #defineL2CAP_CONF_REQ  0x04  
  5. #defineL2CAP_CONF_RSP  0x05  
  6. #defineL2CAP_DISCONN_REQ 0x06  
  7. #defineL2CAP_DISCONN_RSP 0x07  
  8. #defineL2CAP_ECHO_REQ  0x08  
  9. #defineL2CAP_ECHO_RSP  0x09  
  10. #defineL2CAP_INFO_REQ  0x0a  
  11. #defineL2CAP_INFO_RSP  0x0b  

 

 

例二:任意PSM的L2CAP连接间数据的传输

server用来监听指定PSM的连接,并监听数据。同时,利用poll来查看peer是否断掉了。

 

[html] view plain copy
  1. Server:  
  2. #include<stdio.h>  
  3. #include<sys/types.h>          
  4. #include<sys/socket.h>  
  5. #include<stdlib.h>  
  6. #include<poll.h>  
  7.    
  8. #include<bluetooth/bluetooth.h>  
  9. #include<bluetooth/hci.h>  
  10. #include<bluetooth/hci_lib.h>  
  11. #include<bluetooth/l2cap.h>  
  12.    
  13. void *Read_thread(void* pSK);  
  14. intmain(int argc, char** argv)  
  15. {  
  16.     int iRel = 0;  
  17.     int sk = 0;  
  18.     struct sockaddr_l2 local_addr;  
  19.     struct sockaddr_l2 remote_addr;  
  20.     int len;  
  21.     int nsk = 0;  
  22.     pthread_t nth = 0;  
  23.     struct l2cap_options opts;  
  24.     int optlen = 0;  
  25.     int slen = 0;  
  26.     char str[16] = {0};  
  27.     if(argc < 2)  
  28.     {  
  29.         printf("\nUsage:%s psm\n",argv[0]);  
  30.         exit(0);  
  31.     }  
  32.      
  33.     // create l2cap socket  
  34.     sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET,BTPROTO_L2CAP);  //发送数据,使用SOCK_SEQPACKET为好  
  35.     if(sk < 0)  
  36.     {  
  37.         perror("\nsocket():");  
  38.         exit(0);  
  39.     }  
  40.      
  41.     //bind  
  42.     local_addr.l2_family = PF_BLUETOOTH;  
  43.     local_addr.l2_psm = htobs(atoi(argv[argc-1]));  //last psm  
  44.     bacpy(&local_addr.l2_bdaddr,BDADDR_ANY);  
  45.     iRel = bind(sk, (struct sockaddr*)&local_addr, sizeof(struct sockaddr));  
  46.     if(iRel < 0)  
  47.     {  
  48.         perror("\nbind()");  
  49.         exit(0);  
  50.     }  
  51.      
  52.     //get opts  
  53.     // in mtu 和 out mtu.每个包的最大值  
  54.     memset(&opts, 0, sizeof(opts));  
  55.     optlen = sizeof(opts);  
  56.     getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS,&opts, &optlen);  
  57.     printf("\nomtu:[%d]. imtu:[%d].flush_to:[%d]. mode:[%d]\n", opts.omtu, opts.imtu, opts.flush_to,opts.mode);  
  58.      
  59.     //set opts. default value  
  60.     opts.omtu = 0;  
  61.     opts.imtu = 672;  
  62.     if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS,&opts, sizeof(opts)) < 0)  
  63.     {  
  64.         perror("\nsetsockopt():");  
  65.         exit(0);  
  66.     }  
  67.      
  68.     //listen  
  69.     iRel = listen(sk, 10);  
  70.     if(iRel < 0)  
  71.     {  
  72.         perror("\nlisten()");  
  73.         exit(0);  
  74.     }  
  75.      
  76.     len = sizeof(struct sockaddr_l2);  
  77.     while(1)  
  78.     {  
  79.         memset(&remote_addr, 0,sizeof(struct sockaddr_l2));  
  80.         nsk = accept(sk, (structsockaddr*)(&remote_addr), &len);  
  81.         if(nsk < 0)  
  82.         {  
  83.             perror("\naccept():");  
  84.             continue;  
  85.         }  
  86.         ba2str(&(remote_addr.l2_bdaddr),str);  
  87.         printf("\npeerbdaddr:[%s].\n", str);  //得到peer的信息  
  88.         iRel = pthread_create(&nth, NULL,Read_thread, &nsk);  
  89.         if(iRel != 0)  
  90.         {  
  91.             perror("pthread_create():");  
  92.             continue;  
  93.         }  
  94.          
  95.         pthread_detach(nth);  // 分离之  
  96.     }  
  97.    
  98.     return 0;  
  99. }  
  100.    
  101. void *Read_thread(void* pSK)  
  102. {  
  103.     //struct pollfd fds[10];  
  104.     struct  pollfd   fds[100];  
  105.     char buf[1024] = {0};  
  106.     int iRel = 0;  
  107.     int exit_val = 0;  
  108.      
  109.     //fds[0].fd = *(int*)pSK;  
  110.     //fds[0].events = POLLIN | POLLHUP;  
  111.     fds[0].fd  =   (int)(*(int*)pSK);  
  112.     fds[0].events   =  POLLIN   |   POLLHUP;  
  113.      
  114.     while(1)  
  115.     {  
  116.         if(poll(fds, 1, -1) < 0)  
  117.         {  
  118.             perror("\npoll():");  
  119.         }  
  120.         if(fds[0].revents & POLLHUP)  
  121.         {  
  122.             //hang up  
  123.             printf("\n[%d] Hang up\n",*(int*)pSK);  
  124.             close(*(int*)pSK);  
  125.             pthread_exit(&exit_val);  
  126.             break;  
  127.         }  
  128.         if(fds[0].revents & POLLIN)  
  129.         {  
  130.             memset(buf, 0 , 1024);  
  131.             //read data  
  132.             iRel = recv(*(int*)pSK, buf, 572,0);  
  133.             //printf("\nHandle[%d] Receive[%d] data:[%s]", *(int*)pSK, iRel, buf);  
  134.         }  
  135.     }  
  136.      
  137.     return 0;  
  138. }  

 

[html] view plain copy
  1. client:  
  2.    
  3. #include<stdio.h>  
  4. #include<sys/types.h>          
  5. #include<sys/socket.h>  
  6. #include<unistd.h>  
  7.    
  8. #include<bluetooth/bluetooth.h>  
  9. #include<bluetooth/hci.h>  
  10. #include<bluetooth/hci_lib.h>  
  11. #include<bluetooth/l2cap.h>  
  12.    
  13. int main(intargc, char** argv)  
  14. {  
  15.     int sk;  
  16.     int i = 0;  
  17.     char buf[24] = "Sam is Good Guy!";  
  18.     struct sockaddr_l2 local_addr;  
  19.     struct sockaddr_l2 remote_addr;  
  20.     int iRel = 0;  
  21.     if(argc < 3)  
  22.     {  
  23.         printf("\nUsage:%s <bdaddr><PSM>\n", argv[0]);  
  24.         exit(0);  
  25.     }  
  26.      
  27.     sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET,BTPROTO_L2CAP);  
  28.     if(sk < 0)  
  29.     {  
  30.         perror("\nsocket():");  
  31.         exit(0);  
  32.     }  
  33.     //bind. bluetooth好像不许有无名Socket  
  34.     local_addr.l2_family = PF_BLUETOOTH;  
  35.     bacpy(&local_addr.l2_bdaddr,BDADDR_ANY);  
  36.     iRel = bind(sk, (struct sockaddr *)&local_addr,sizeof(struct sockaddr));  
  37.     if(iRel < 0)  
  38.     {  
  39.         perror("\nbind()");  
  40.         exit(0);  
  41.     }  
  42.      
  43.     memset(&remote_addr, 0, sizeof(structsockaddr_l2));  
  44.     remote_addr.l2_family = PF_BLUETOOTH;  
  45.     str2ba(argv[1], &remote_addr.l2_bdaddr);  
  46.     remote_addr.l2_psm = htobs(atoi(argv[argc-1]));  
  47.      
  48.     connect(sk, (structsockaddr*)&remote_addr, sizeof(struct sockaddr_l2));  
  49.     for(i = 0; i < 60; i++)  
  50.     {  
  51.         iRel = send(sk, buf, strlen(buf)+1, 0);  
  52.         printf("Send [%d] data\n",strlen(buf)+1);  
  53.         sleep(1);  
  54.     }  
  55.      
  56.     close(sk);  
  57.      
  58.     return 0;  
  59. }  
  60.    

 

注意:

1. 在Linux 网络编程中,主动发起连接方,因为不关心地址具体是什么,所以可以作为无名socket,也就是说可以不bind. 但Bluetooth则不可以,一定需要bind.

2. poll可以查出连接断连,但需要注意:断开的revent值为:11001B。也就是说:POLLIN |POLLERR |POLLHUP。

3. 被连接一方,一定要指定PSM。

 

 

 

关于sdp

 

Servicediscovery机制提供client应用程序侦测server应用程序提供的服务的能力,并且能够得到服务的特性。服务的品质包含服务type或服务class.

SDP也提供SDP server与SDP client之间的通讯。SDP server维护着一个服务条目(service record)列表.每个服务条目描述一个单独的服务属性。 SDP client可以通过发送SDP request来得到服务条目。

如果一个client或者依附于client之上的应用程序决定使用某个service. 它创建一个单独的连接到service提供者。 SDP 只提供侦测Service的机制,但不提供如何利用这些Service的机制。这里其实是说:SDP只提供侦测Service的办法,但如何用,SDP不管。

每个Bluetooth Device最多只能拥有一个SDP Server。如果一个Bluetooth Device只担任Client,那它不需要SDP Server。但一个Bluetooth Device可以同时担当SDP Server和SDP client.

 

ServiceRecord(Service 条目):

一个service是一个实体为另一个实体提供信息,执行动作或控制资源。一个service可以由软件,硬件或软硬件结合提供。

所有的Service信息都包含于一个Service Record内。一个Service Record 包含一个Service attribute(Service属性) list.


 

在一个SDP Server内,每个Service Record拥有一个32-bit的唯一性数据。通常,这个唯一性只是在每个SDP Server内部。 如果SDP Server S1 和SDP Server S2拥有同样的一个Service Record。那他们在不同SDP Sever内的独特数值并不一定相同。

SDP在SDP Server增加或减少Service Record时,并不会通知SDP client.

 

ServiceAttribute(Service 属性):

每个Service属性描述servcie的特性.一个Service Attribute由2部分:

AttributeID + Attribute Value。



AttributeID:16-bit无符号整数,用于区别一个Service Record内的其它属性。

AttributeValue:Attribute值。

 

ServiceClass:

每个Service 都是某个Service Class的实例. Service Class定义了Service Record中包含的Service 属性。属性ID,属性值都被定义好了。

每个Service Class也有一个独特ID。这个Service Class标识符包含在属性值ServiceClassIDList属性中。并描绘为UUID。自从Service Record中的属性格式以及含义依赖于Service Class后,ServiceClassIDList属性变得非常重要。

 

SearchingFor Service:

ServiceSearch transaction(事务)允许client得到Service Record Handle。一旦SDP Client得到Service Record Handle,它就可以请求这个Record内具体属性的值。

如知道某个属性值UUID,则可以通过查找UUID查到这个属性。

UUID: universally uniqueidentifier.(唯一性标识符)

 

SDP协议栈使用request/response模式工作,每个传输过程包括一个request protocoldata unit(PDU)和一个response PDU. SDP使用L2CAP连接传输数据。在发送Request PDU但未收到Response PDU之前,不能向同一个server再发送Request PDU。


 


PDU ID:用来识别PDU。

TransactionID:用来识别Request PUD以及Response PUD。并用来对比某个Response PUD是否对应着Request PUD。

 

 

关于rfcomm

 

测试端输入字符串,并显示在服务端

 

[html] view plain copy
  1. Client:  
  2.    
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <unistd.h>  
  6. #include<sys/socket.h>  
  7. #include<bluetooth/bluetooth.h>  
  8. #include<bluetooth/rfcomm.h>  
  9. int main( int argc , char**argv)  
  10. {  
  11.     struct sockaddr_rc addr={0};  
  12.     int s,status;  
  13.     char *dest,*buf; //="00:11:67:32:61:62";  
  14.      
  15.     if(argc==2)  
  16.     {  
  17.         dest=argv[1];  
  18.     }  
  19.     else  
  20.     {  
  21.         printf("prarmerror/n");  
  22.         exit(1);  
  23.     }  
  24.     // allocate a socket  
  25.     s=socket(PF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM);  
  26.     if(s<0)  
  27.     {  
  28.         perror("createsocket error");  
  29.         exit(1);  
  30.     }    
  31.     // set the connection parameters(who to connect to)  
  32.     addr.rc_family=AF_BLUETOOTH;  
  33.     addr.rc_channel=(uint8_t)1;  
  34.     str2ba(dest,&addr.rc_bdaddr);  
  35.     // connect to server  
  36.     printf("connectting.../n");  
  37.     status=connect(s,(struct sockaddr *)&addr,sizeof(addr));  
  38.     // send a message  
  39.     if(status==0)  
  40.     {  
  41.         printf("scuess!/n");  
  42.         status=write(s,"hello!",6);  
  43.    
  44.         do  
  45.         {  
  46.             scanf("%s",buf);  
  47.             status=write(s,buf,strlen(buf));  
  48.             if(status<0) perror("uh oh");  
  49.         }while(strcmp(buf,"goodbye")!=0);  
  50.     }  
  51.     else  
  52.     {  
  53.         printf("Failed!/n");  
  54.     }  
  55.      
  56.     close(s);  
  57.     return 0;  
  58. }  

 

[html] view plain copy
  1. Server:  
  2.    
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <unistd.h>  
  6. #include<sys/socket.h>  
  7. #include<bluetooth/bluetooth.h>  
  8. #include<bluetooth/rfcomm.h>  
  9. int main (int argc,char**argv)  
  10. {  
  11.     struct sockaddr_rc loc_addr ={0},rem_addr={0};  
  12.     char buf[1024] ={0};//,*addr;  
  13.     int s,client, bytes_read,result;  
  14.     int opt = sizeof(rem_addr);  
  15.      
  16.     printf("Creating socket.../n");  
  17.     s =socket(PF_BLUETOOTH,SOCK_STREAM,BTPROTO_RFCOMM);  
  18.     if(s<0)  
  19.     {  
  20.         perror("createsocket error");  
  21.        exit(1);  
  22.     }  
  23.     else  
  24.     {  
  25.        printf("success!/n");  
  26.     }  
  27.      
  28.     loc_addr.rc_family=AF_BLUETOOTH;  
  29.     loc_addr.rc_bdaddr=*BDADDR_ANY;  
  30.     loc_addr.rc_channel=(uint8_t)1;  
  31.      
  32.     printf("Binding socket.../n");  
  33.     result=bind(s,(struct sockaddr *)&loc_addr,sizeof(loc_addr));  
  34.     if(result<0)  
  35.     {  
  36.        perror("bind socket error:");  
  37.        exit(1);  
  38.     }  
  39.     else  
  40.     {  
  41.        printf("success!/n");  
  42.     }  
  43.      
  44.     /*result=ba2str(&loc_addr.rc_bdaddr,addr);  
  45.     if(result<0)  
  46.     {  
  47.         perror("addrconvert error");  
  48.        exit(1);  
  49.     }  
  50.     printf("localaddr is:%s/n",addr);*/  
  51.     printf("Listen... ");  
  52.     result=listen(s,1);  
  53.     if(result<0)  
  54.     {  
  55.        printf("error:%d/n:",result);  
  56.        perror("listen error:");  
  57.        exit(1);  
  58.     }  
  59.     else  
  60.     {  
  61.        printf("requested!/n");  
  62.     }  
  63.      
  64.     printf("Accepting.../n");  
  65.     clientaccept(s,(struct sockaddr *)&rem_addr,&opt);  
  66.     if(client<0)  
  67.     {  
  68.         perror("accepterror");  
  69.        exit(1);  
  70.     }  
  71.     else  
  72.     {  
  73.        printf("OK!/n");  
  74.     }  
  75.     ba2str(&rem_addr.rc_bdaddr,buf);  
  76.     fprintf(stderr,"accepted connection from %s /n",buf);  
  77.     memset(buf,0,sizeof(buf));  
  78.      
  79.     while(1)  
  80.     {  
  81.        bytes_read = read(client,buf,sizeof(buf));  
  82.        if(bytes_read>0)  
  83.        {  
  84.             printf("received[%s]/n",buf);  
  85.             if(strcmp(buf,"goodbye")==0)  
  86.                 exit(1);  
  87.             memset(buf,0,bytes_read);  
  88.        }  
  89.     }  
  90.      
  91.     close(client);  
  92.     close(s);  
  93.     return 0 ;  
  94. }  

 

 

阅读(2328) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~