继续上文的socke的创建之后,我们自然而然就会想到下面的工作了,没错就是bind。
5.6 l2cap的bind分析
按照国际惯例,在建了socket之后,必然会有bind,哈哈~~
if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, opts->cid, err) < 0)
这个函数如下:
static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, uint16_t cid, GError **err) { struct sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, src); //cid是0,psm是0? if (cid) addr.l2_cid = htobs(cid); else addr.l2_psm = htobs(psm); //bind,这里仍然会调用到kernel中去,详细见5.6.1的分析 if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { ERROR_FAILED(err, "l2cap_bind", errno); return -1; } return 0; }
5.6.1 kernel中 bind的分析
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { //得到对应的sock和chan struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct sockaddr_l2 la; int len, err = 0; BT_DBG("sk %p", sk); //检查一下传入的参数 if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; memset(&la, 0, sizeof(la)); len = min_t(unsigned int, sizeof(la), alen); //把传入的内容拷贝到la中 memcpy(&la, addr, len); //两个不能都有值,我们两个都是0 if (la.l2_cid && la.l2_psm) return -EINVAL; lock_sock(sk); //这个开始就是open,这里只是double check一下 if (sk->sk_state != BT_OPEN) { err = -EBADFD; goto done; } //若是传入了psm if (la.l2_psm) { __u16 psm = __le16_to_cpu(la.l2_psm); //psm必须是奇数,并且高位是0,这个是spec规定的。具体的介绍见5.6.2 /* PSM must be odd and lsb of upper byte must be 0 */ if ((psm & 0x0101) != 0x0001) { err = -EINVAL; goto done; } /* Restrict usage of well-known PSMs */ //psm范围的检查以及权限的检查 if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { err = -EACCES; goto done; } } //若是有cid,就add cid,否则就add psm,关于cid的概念,我们在5.6.3中分析 if (la.l2_cid) err = l2cap_add_scid(chan, la.l2_cid); else //这里不管有没有psm都是可以的,没有就生成一个,这里是psm的赋值,详细分析见5.6.4 err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm); if (err < 0) goto done; //这个时特殊的psm值,sdp if (__le16_to_cpu(la.l2_psm) == 0x0001 || __le16_to_cpu(la.l2_psm) == 0x0003) chan->sec_level = BT_SECURITY_SDP; //这里设置的是源 bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); //chan的state设为bound,就是binded了 chan->state = BT_BOUND; sk->sk_state = BT_BOUND; done: release_sock(sk); return err; }
5.6.2 spec中psm的规定
Psm全称为Protocol/ServiceMultiplexer。在spec中是这样描述的:
The PSM fieldis at least two octets in length. The structure of the PSM field is based onthe ISO 3309 extension mechanism for address fields. All PSM values shall beODD, that is, the least significant bit of the least significant octet must be’1’. Also, all PSM values shall have the least significant bit of the mostsignificant octet equal to ’0’. This allows the PSM field to be extended beyond16 bits. PSM values are separated into two ranges. Valid values in the firstrange are assigned by the Bluetooth SIG and indicate protocols. The secondrange of values are dynamically allocated and used in conjunction with theService Discovery Protocol (SDP). The dynamically assigned values may be usedto support multiple implementations of a particular protocol.
这段话大概的意思是PSM的长度最少是2byte,它的值应当是奇数,就是最低的byte的最低位必须为1.另外,psm的最高byte的最低位应当为0.它可以比2byte长,psm由两个范围段组成,第一个范围段是SIG用来表示对应protocol的,第二个范围段是动态申请的和SDP结合使用。这个值用来支持特定protocol的不同实现。这两个范围段的设置如下:
所以,我们在申请psm的时候都是从0x1001开始申请的。原因就是0x0001~0x0eff都是被SIG保留的。那么这些保留的值都各自对应了哪些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 |
ATT |
0X001F |
3DSP |
0X0021 |
5.6.3 spec中cid的规定
CID的全称为ChannelIdentifer,他是用来标明不同的L2CAP通道的,每一个L2CAP通道都需要有一个不同的CID来表示。在spec中对CID的主要描述如下:
A channel identifier(CID) is the local name representing a logical channel endpoint on the device.The null identifier (0x0000) shall never be used as a destination endpoint.Identifiers from 0x0001 to 0x003F are reserved for specific L2CAP functions.These channels are referred to as Fixed Channels. At a minimum, the L2CAPSignaling channel (Fixed Channel 0x0001) or the L2CAP LE Signaling channel(Fixed Channel 0x0005) shall be supported. If Fixed Channel 0x0005 issupported, then Fixed Channels 0x0004 and 0x0006 shall be supported (see Table 2.1). Otherfixed channels may be supported. The Information Request / Response mechanism(described in Section 4.10 and Section 4.11) shall be used to determine which fixed channels aremote device supports over the ACL-U logical link.
在这段的描述中,我们可以发现0x0000是不能使用的,0x0001~0x003F是被征用的,他们被叫做FixedChannels,就是老弱病残孕专座,一般人还是不要去坐(不太恰当,在我们国家大家还是都会去坐的,哈哈~~)。这其中L2CAP Signaling channel(0x0001)和LESignaling channel(0x0005)是应当要支持的。若是支持0x0005(LE设备),那么0x004和0x006也应当被支持。其他的都是可能支持。Fix channel究竟有哪些呢,具体见下表:
总的来说,要建一个L2CAP通道就需要一个对应的cid值来表示,大概就是这个意思。
5.6.4 bluez中psm赋值的实现
在5.6.2中,我们有详细介绍psm在spec中是如何定义的,下面我们继续来看psm在bluez的代码中是如何实现的。
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) { int err; write_lock_bh(&chan_list_lock); //若是有psm,又发现了和这个psm对应的chan,就直接返回in use的错误 if (psm && __l2cap_global_chan_by_addr(psm, src)) { err = -EADDRINUSE; goto done; } if (psm) { //若是有psm,就是设置chan的psm和sport值 chan->psm = psm; chan->sport = psm; err = 0; } else { u16 p; err = -EINVAL; for (p = 0x1001; p < 0x1100; p += 2) //若是没有,就是从低到高,找没有用到的psm。有了spec的解释,这里的为什么从1001开始就很清楚了吧 if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) { chan->psm = cpu_to_le16(p); chan->sport = cpu_to_le16(p); err = 0; break; } } done: write_unlock_bh(&chan_list_lock); return err; }
至此,bind的分析就全部结束了,其实就是把对应的psm和cid与下面申请的protocol和L2cap关联起来,若是没有就申请对应的psm或者cid值。