篇文章《kernel中bluetooth的初始化》一文中晓东和大家分享了HCI层,L2CAP层以及SCO层的初始化流程,今天晓东继续和大家一起来看rfcomm层的初始化流程。
在正式开始之前,我们先来看一下rfcomm层是什么,百度百科是这样介绍rfcomm的:“一个基于欧洲电信标准协会ETSI07.10规程的串行线性仿真协议。此协议提供RS232控制和状态信号,如基带上的损坏,CTS以及数据信号等,为上层业务(如传统的串行线缆应用)提供了传送能力。
RFCOMM是一个简单传输协议,其目的为了解决如何在两个不同设备上的应用程序之间保证一条完整的通信路径,并在它们之间保持一通信段的问题。”
对这段内容,大家简单地读一下就可以了,若想详细了解rfcomm层的作用和内容,推荐大家去看TS07.10和rfcomm两个spec,他们上面讲解得还是蛮全面的。
晓东这里针对rfcomm的理论内容就只给大家看一张图,这张图来源于rfcomm的spec。
从这张图,我们可以清晰地看到rfcomm是基于L2CAP层的,在他们之下就是Baseband了,当然和Baseband的交互必然就少不了HCI层的支援了。Ok,大体的架构就是这样的,让我们开始代码的阅读吧。
同样的,先找到代码的位置kernel/net/bluetooth/rfcomm文件夹,先看里面的core.c,初始化的代码就是这句了module_init(rfcomm_init);,同样不多介绍module_init的作用,请自己google或者百度。
- static int __init rfcomm_init(void)
- {
- int err;
-
- hci_register_cb(&rfcomm_cb);
-
- rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
- ……
-
-
- if (bt_debugfs) {
- rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
- bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops);
- if (!rfcomm_dlc_debugfs)
- BT_ERR("Failed to create RFCOMM debug file");
- }
-
- err = rfcomm_init_ttys();
- if (err < 0)
- goto stop;
-
-
-
-
- err = rfcomm_init_sockets();
- if (err < 0)
- goto cleanup;
- ……
- }
1、 rfcomm_run
这个内核线程在没有意外的情况下将一直在后台运行着,下面我们就来详细看一下它的运转究竟干了些什么。初略一看,是不是感觉代码不多,很简单啊,呵呵~~,慢慢来看吧,其实真的好多内容。
- static int rfcomm_run(void *unused)
- {
- BT_DBG("");
-
-
- set_user_nice(current, -10);
-
- rfcomm_add_listener(BDADDR_ANY);
- while (1) {
-
- set_current_state(TASK_INTERRUPTIBLE);
-
-
- if (kthread_should_stop())
- break;
-
-
- rfcomm_process_sessions();
-
- schedule();
- }
- __set_current_state(TASK_RUNNING);
-
- rfcomm_kill_listener();
-
- return 0;
- }
1.1、rfcomm_add_listener
该函数主要工作是新建一个L2CAP的socket,然后在该socket上进行监听。具体看代码分析
- static int rfcomm_add_listener(bdaddr_t *ba)
- {
- ……
-
-
- err = rfcomm_l2sock_create(&sock);
- ……
-
-
- bacpy(&addr.l2_bdaddr, ba);
- addr.l2_family = AF_BLUETOOTH;
- addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
- addr.l2_cid = 0;
- err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
- ……
-
- sk = sock->sk;
- lock_sock(sk);
-
- l2cap_pi(sk)->chan->imtu = l2cap_mtu;
- release_sock(sk);
-
-
-
- err = kernel_listen(sock, 10);
- ……
-
-
-
- s = rfcomm_session_add(sock, BT_LISTEN);
- ……
- rfcomm_session_hold(s);
- return 0;
- failed:
- sock_release(sock);
- return err;
- }
1.1.1 rfcomm_l2sock_create
该函数最终会调用l2cap的creat函数去创建socket
- static int rfcomm_l2sock_create(struct socket **sock)
- {
-
-
-
-
-
- err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
- if (!err) {
- struct sock *sk = (*sock)->sk;
-
- sk->sk_data_ready = rfcomm_l2data_ready;
- sk->sk_state_change = rfcomm_l2state_change;
- }
- return err;
- }
我们继续来看一下l2cap_sock.c中的create函数
- static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
- int kern)
- {
- struct sock *sk;
-
- BT_DBG("sock %p", sock);
-
- sock->state = SS_UNCONNECTED;
-
- if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM &&
- sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
- return -ESOCKTNOSUPPORT;
-
- if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
- return -EPERM;
-
- sock->ops = &l2cap_sock_ops;
-
-
- sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC);
- if (!sk)
- return -ENOMEM;
-
- l2cap_sock_init(sk, NULL);
- return 0;
- }
1.1.1.1 l2cap_sock_alloc
这个函数还是比较简单的,就是创建一个sock,初始化了他的一些内容。同时还创建了一个l2cap的channel,并且把它加入到了chan_list链表中去了
- static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
- {
- struct sock *sk;
- struct l2cap_chan *chan;
-
- sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
- if (!sk)
- return NULL;
-
- sock_init_data(sock, sk);
-
- INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
-
- sk->sk_destruct = l2cap_sock_destruct;
-
- sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;
-
- sock_reset_flag(sk, SOCK_ZAPPED);
- sk->sk_protocol = proto;
-
- sk->sk_state = BT_OPEN;
-
- chan = l2cap_chan_create(sk);
- if (!chan) {
-
- l2cap_sock_kill(sk);
- return NULL;
- }
-
- l2cap_pi(sk)->chan = chan;
-
- return sk;
- }
1.1.2 kernel_bind
该函数其实就是把addr相关的内容绑定到刚刚新建的socket,说白了就是通过传入一些参数初始化一些socket的内容。这些内容也就是chan的psm和cid值。
- static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
- {
- ……
- if (!addr || addr->sa_family != AF_BLUETOOTH)
- return -EINVAL;
-
- memset(&la, 0, sizeof(la));
- len = min_t(unsigned int, sizeof(la), alen);
-
- memcpy(&la, addr, len);
-
-
- if (la.l2_cid && la.l2_psm)
- return -EINVAL;
-
- lock_sock(sk);
-
- if (sk->sk_state != BT_OPEN) {
- err = -EBADFD;
- goto done;
- }
-
- if (la.l2_psm) {
- __u16 psm = __le16_to_cpu(la.l2_psm);
-
-
-
-
-
- if ((psm & 0x0101) != 0x0001) {
- err = -EINVAL;
- goto done;
- }
-
-
- if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) {
- err = -EACCES;
- goto done;
- }
- }
-
- if (la.l2_cid)
-
- err = l2cap_add_scid(chan, la.l2_cid);
- else
-
-
-
-
- err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm);
-
- if (err < 0)
- goto done;
-
- 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 = BT_BOUND;
- sk->sk_state = BT_BOUND;
-
- done:
- release_sock(sk);
- return err;
- }
至此,我们可以总结一下,rfcomm和L2CAP或者HCI的初始化相差的地方在于,除了那些debug文件和proto相关的内容外,它启动了一个rfcomm_run的thread在后台不停地运行。该thread在一开始就是创建了一个BDADDR_ANY的L2CAP socket和socket channel,并在该socket上进行了listen。同时该thread还会不停地去监听rfcomm_session list的各个状态的变化,同时根据状态的变化去做相应的处理。