1. 程式人生 > >[Android原始碼分析]L2CAP的bind分析以及psm和cid的介紹和實現

[Android原始碼分析]L2CAP的bind分析以及psm和cid的介紹和實現

繼續上文的socke的建立之後,我們自然而然就會想到下面的工作了,沒錯就是bind。

5.6 l2capbind分析

    按照國際慣例,在建了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 specpsm的規定

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 speccid的規定

    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 channel0x0001)和LESignaling channel0x0005)是應當要支援的。若是支援0x0005LE裝置),那麼0x0040x006也應當被支援。其他的都是可能支援。Fix channel究竟有哪些呢,具體見下表:


總的來說,要建一個L2CAP通道就需要一個對應的cid值來表示,大概就是這個意思。

5.6.4 bluezpsm賦值的實現

    在5.6.2中,我們有詳細介紹psmspec中是如何定義的,下面我們繼續來看psmbluez的程式碼中是如何實現的。

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的分析就全部結束了,其實就是把對應的psmcid與下面申請的protocolL2cap關聯起來,若是沒有就申請對應的psm或者cid值。