Kernel中rfcomm层的初始化

篇文章《kernelbluetooth的初始化》一文中晓东和大家分享了HCI层,L2CAP层以及SCO层的初始化流程,今天晓东继续和大家一起来看rfcomm层的初始化流程。

         在正式开始之前,我们先来看一下rfcomm层是什么,百度百科是这样介绍rfcomm的:“一个基于欧洲电信标准协会ETSI07.10规程的串行线性仿真协议。此协议提供RS232控制和状态信号,如基带上的损坏,CTS以及数据信号等,为上层业务(如传统的串行线缆应用)提供了传送能力。

RFCOMM是一个简单传输协议,其目的为了解决如何在两个不同设备上的应用程序之间保证一条完整的通信路径,并在它们之间保持一通信段的问题。”

对这段内容,大家简单地读一下就可以了,若想详细了解rfcomm层的作用和内容,推荐大家去看TS07.10rfcomm两个spec,他们上面讲解得还是蛮全面的。

         晓东这里针对rfcomm的理论内容就只给大家看一张图,这张图来源于rfcommspec

从这张图,我们可以清晰地看到rfcomm是基于L2CAP层的,在他们之下就是Baseband了,当然和Baseband的交互必然就少不了HCI层的支援了。Ok,大体的架构就是这样的,让我们开始代码的阅读吧。

         同样的,先找到代码的位置kernel/net/bluetooth/rfcomm文件夹,先看里面的core.c,初始化的代码就是这句了module_init(rfcomm_init);,同样不多介绍module_init的作用,请自己google或者百度。

[cpp]  view plain copy
  1. static int __init rfcomm_init(void)  
  2. {  
  3.     int err;  
  4.     //把rfcomm_cb加入到hci_cb_list链表中,不详细解释  
  5.     hci_register_cb(&rfcomm_cb);  
  6.     //创建并启动rfcomm_run线程,rfcomm_run的操作,详见1  
  7.     rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");  
  8. ……  
  9. //bt_debugfs就是sys/kernel/debug/bluetooth,在前一篇中介绍过  
  10. //这里就是在这个文件夹下再建一个rfcomm_dlc的文件。不解释  
  11.     if (bt_debugfs) {  
  12.         rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,  
  13.                 bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops);  
  14.         if (!rfcomm_dlc_debugfs)  
  15.             BT_ERR("Failed to create RFCOMM debug file");  
  16.     }  
  17. //rfcomm tty的初始化,见2  
  18.     err = rfcomm_init_ttys();  
  19.     if (err < 0)  
  20.         goto stop;  
  21.     //rfcomm socket的init,和前一篇的hci是一样的  
  22. //主要工作就是向proto_list中加入了rfcomm  
  23. //初始化了bt_proto中BTPROTO_RFCOMM的内容  
  24. //新建了sys/kernel/debug/bluetooth/rfcomm文件  
  25.     err = rfcomm_init_sockets();  
  26.     if (err < 0)  
  27.         goto cleanup;  
  28. ……  
  29. }  

1、 rfcomm_run

这个内核线程在没有意外的情况下将一直在后台运行着,下面我们就来详细看一下它的运转究竟干了些什么。初略一看,是不是感觉代码不多,很简单啊,呵呵~~,慢慢来看吧,其实真的好多内容。

[cpp]  view plain copy
  1. static int rfcomm_run(void *unused)  
  2. {  
  3.     BT_DBG("");  
  4. //通常而言,kthread_run创建并运行的线程的优先级是SCHED_NORMAL,也就是优先级值为0的线程  
  5.     //设置当前的进程优先级为-10  
  6.     set_user_nice(current, -10);  
  7. //增加监听,详细分析见1.1  
  8.     rfcomm_add_listener(BDADDR_ANY);  
  9.     while (1) {  
  10.         //设置当前的线程为可中断的睡眠态,也就是睡眠了  
  11.         set_current_state(TASK_INTERRUPTIBLE);  
  12.         //看一下当前线程是不是stop了,若是stop了就结束线程  
  13.         //一般一些出错啊,exit之类的会调用stop  
  14.         if (kthread_should_stop())  
  15.             break;  
  16.         //检测rfcomm的session list中各个seesion的状态的变换,并做相应的操作,这个在以后的rfcomm连接中再详细介绍  
  17.         /* Process stuff */  
  18.         rfcomm_process_sessions();  
  19.         //调度别的线程去占用cpu  
  20.         schedule();  
  21.     }  
  22.     __set_current_state(TASK_RUNNING);  
  23.   
  24.     rfcomm_kill_listener();  
  25.   
  26.     return 0;  
  27. }  

1.1rfcomm_add_listener

         该函数主要工作是新建一个L2CAPsocket,然后在该socket上进行监听。具体看代码分析

[cpp]  view plain copy
  1. static int rfcomm_add_listener(bdaddr_t *ba)  
  2. {  
  3. ……  
  4.     /* Create socket */  
  5.     //新建L2CAP层的socket,详细见1.1.1  
  6.     err = rfcomm_l2sock_create(&sock);  
  7. ……  
  8.     //bind addr to socket,详细见1.1.2  
  9.     /* Bind socket */  
  10.     bacpy(&addr.l2_bdaddr, ba);  
  11.     addr.l2_family = AF_BLUETOOTH;  
  12.     addr.l2_psm    = cpu_to_le16(RFCOMM_PSM); //psm是3,表示rfcomm,有蓝牙sig规定  
  13.     addr.l2_cid    = 0;  
  14.     err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));  
  15. ……  
  16.     /* Set L2CAP options */  
  17.     sk = sock->sk;  
  18.     lock_sock(sk);  
  19.     //设置l2cap channel的mtu(max transfer unit)  
  20.     l2cap_pi(sk)->chan->imtu = l2cap_mtu;  
  21.     release_sock(sk);  
  22.     //开始在该socket上listen,最多有10个连接  
  23.     //主要就是设置了一下socket和chan的state,然后设了一下最大的连接  
  24.     /* Start listening on the socket */  
  25.     err = kernel_listen(sock, 10);  
  26. ……  
  27.     //新建一个rfcomm的session并把它加入到session_list中  
  28.     //同时把该session和socket进行了关联  
  29.     /* Add listening session */  
  30.     s = rfcomm_session_add(sock, BT_LISTEN);  
  31. ……  
  32.     rfcomm_session_hold(s);  
  33.     return 0;  
  34. failed:  
  35.     sock_release(sock);  
  36.     return err;  
  37. }  

1.1.1 rfcomm_l2sock_create

该函数最终会调用l2capcreat函数去创建socket

[cpp]  view plain copy
  1. static int rfcomm_l2sock_create(struct socket **sock)  
  2. {  
  3.     //创建L2CAP层  
  4.     //l2cap_sock.c -----create  
  5. //family—PF_BLUETOOTH  
  6. //type—SOCK_SEQPACKET  
  7. //proto—BTPROTO_L2CAP  
  8.     err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);  
  9.     if (!err) {  
  10.         struct sock *sk = (*sock)->sk;  
  11.         //data ready和state change都设为rfcomm这边的回调函数  
  12.         sk->sk_data_ready   = rfcomm_l2data_ready;  
  13.         sk->sk_state_change = rfcomm_l2state_change;  
  14.     }  
  15.     return err;  
  16. }  

我们继续来看一下l2cap_sock.c中的create函数

[cpp]  view plain copy
  1. static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,  
  2.                  int kern)  
  3. {  
  4.     struct sock *sk;  
  5.   
  6.     BT_DBG("sock %p", sock);  
  7.     //socket的state先设为SS_UNCONNECTED  
  8.     sock->state = SS_UNCONNECTED;  
  9.     //检查socket的type  
  10.     if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM &&  
  11.             sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)  
  12.         return -ESOCKTNOSUPPORT;  
  13.     //SOCK_RAW必须要NET_RAW权限才能建  
  14.     if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))  
  15.         return -EPERM;  
  16.     //ops的初始化  
  17.     sock->ops = &l2cap_sock_ops;  
  18.     //l2cap sock的申请,创建了一个L2cap的socket和一个l2cap的channel  
  19.     //可以详细见1.1.1.1  
  20.     sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC);  
  21.     if (!sk)  
  22.         return -ENOMEM;  
  23.     //l2cap channel的初始化,就不详细解释了  
  24.     l2cap_sock_init(sk, NULL);  
  25.     return 0;  
  26. }  

1.1.1.1 l2cap_sock_alloc

这个函数还是比较简单的,就是创建一个sock,初始化了他的一些内容。同时还创建了一个l2capchannel,并且把它加入到了chan_list链表中去了

[cpp]  view plain copy
  1. static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)  
  2. {  
  3.     struct sock *sk;  
  4.     struct l2cap_chan *chan;  
  5.     //申请sock  
  6.     sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);  
  7.     if (!sk)  
  8.         return NULL;  
  9.     //sock init  
  10.     sock_init_data(sock, sk);  
  11.     //初始化accept_q  
  12.     INIT_LIST_HEAD(&bt_sk(sk)->accept_q);  
  13.     //destruct的初始化  
  14.     sk->sk_destruct = l2cap_sock_destruct;  
  15.     //发送的timeout,40s  
  16.     sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT;  
  17.     //把flag位reset  
  18.     sock_reset_flag(sk, SOCK_ZAPPED);  
  19.     sk->sk_protocol = proto;  
  20.     //sk_state初始化为BT_OPEN  
  21.     sk->sk_state = BT_OPEN;  
  22.     //create channel,并把它加入到chan_list链表中  
  23.     chan = l2cap_chan_create(sk);  
  24.     if (!chan) {  
  25.         //没有能够create,则kill  
  26.         l2cap_sock_kill(sk);  
  27.         return NULL;  
  28.     }  
  29.     //设置chan  
  30.     l2cap_pi(sk)->chan = chan;  
  31.   
  32.     return sk;  
  33. }  

1.1.2 kernel_bind

该函数其实就是把addr相关的内容绑定到刚刚新建的socket,说白了就是通过传入一些参数初始化一些socket的内容。这些内容也就是chanpsmcid值。

[cpp]  view plain copy
  1. static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)  
  2. {  
  3. ……  
  4.     if (!addr || addr->sa_family != AF_BLUETOOTH)  
  5.         return -EINVAL;  
  6.   
  7.     memset(&la, 0, sizeof(la));  
  8.     len = min_t(unsigned intsizeof(la), alen);  
  9.     //两者的小的拷贝到sockaddr_l2中  
  10.     memcpy(&la, addr, len);  
  11.     //若是有了cid和psm就出错返回了  
  12. //这里是传入了psm,但是cid还没有传入  
  13.     if (la.l2_cid && la.l2_psm)  
  14.         return -EINVAL;  
  15.   
  16.     lock_sock(sk);  
  17.     //检查一下sk_state是否为BT_OPEN  
  18.     if (sk->sk_state != BT_OPEN) {  
  19.         err = -EBADFD;  
  20.         goto done;  
  21.     }  
  22.     //若是已经有了psm  
  23.     if (la.l2_psm) {  
  24.         __u16 psm = __le16_to_cpu(la.l2_psm);  
  25.   
  26.         /* PSM must be odd and lsb of upper byte must be 0 */  
  27.         //检查psm的值  
  28.         //psm的值必须在0x1001以上,且是奇数  
  29.         //同时最高有效byte的最低位必须为0  
  30.         if ((psm & 0x0101) != 0x0001) {  
  31.             err = -EINVAL;  
  32.             goto done;  
  33.         }  
  34.         //确认psm的有效性  
  35.         /* Restrict usage of well-known PSMs */  
  36.         if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) {  
  37.             err = -EACCES;  
  38.             goto done;  
  39.         }  
  40.     }  
  41.     //若是有l2_cid  
  42.     if (la.l2_cid)  
  43.         //把cid保存到chan->cid中  
  44.         err = l2cap_add_scid(chan, la.l2_cid);  
  45.     else  
  46.         //add psm  
  47.         //主要是赋值chan->psm和chan->sport  
  48.         //la.l2psm为0时会按照规定申请  
  49. //所以,这个函数主要就是初始化psm的值  
  50.         err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm);  
  51.   
  52.     if (err < 0)  
  53.         goto done;  
  54.     //psm若是1和3,则sec level设为sdp  
  55.     if (__le16_to_cpu(la.l2_psm) == 0x0001 ||  
  56.                 __le16_to_cpu(la.l2_psm) == 0x0003)  
  57.         chan->sec_level = BT_SECURITY_SDP;  
  58. //socket的src  
  59.     bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);  
  60.     //chan的state和sk的sk_state都设为BT_BOUND  
  61.     chan->state = BT_BOUND;  
  62.     sk->sk_state = BT_BOUND;  
  63.   
  64. done:  
  65.     release_sock(sk);  
  66.     return err;  
  67. }  

至此,我们可以总结一下,rfcommL2CAP或者HCI的初始化相差的地方在于,除了那些debug文件和proto相关的内容外,它启动了一个rfcomm_runthread在后台不停地运行。该thread在一开始就是创建了一个BDADDR_ANYL2CAP socketsocket channel,并在该socket上进行了listen。同时该thread还会不停地去监听rfcomm_session list的各个状态的变化,同时根据状态的变化去做相应的处理。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值