Bluez HID分析--Linux kernel部分
发布日期:2021-09-27 14:12:43 浏览次数:6 分类:技术文章

本文共 6288 字,大约阅读时间需要 20 分钟。

本文介绍蓝牙协议栈Bluez在linux中实现HID功能的kernel部分。在linux kernel中,Bluez对HID的实现代码在/net/bluetooth/hidp文件夹中,主要包括sock.c,core.c和hidp.h三个文件。Bluez提供了一个socket接口,用户空间程序通过使用该socket控制HID。该socket使用的协议编号为BTPROTO_HIDP。1.  初始化HIDP的初始化在sock.c的hidp_init_sockets函数。int __init hidp_init_sockets(void)err = proto_register(&hidp_proto, 0); // 注册proto,可忽略// 注册一个BTPROTO_HIDP协议号err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops);bt_sock_register是bluez内部的函数,其作用是将传入到net_proto_family结构体登记到全局数组bt_proto中。int bt_sock_register(int proto, const struct net_proto_family *ops)      。。。      bt_proto[proto] = ops;      。。。这里用到的思想与字符设备驱动中的misc驱动类似,不再赘述。注册完成之后,用户空间就能够通过标准socket操作得到HIDP的socket:Int sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);下面再看一下结构体hidp_sock_family_ops的内容:static const struct net_proto_family hidp_sock_family_ops = {            .family  = PF_BLUETOOTH,            .owner  = THIS_MODULE,            .create  = hidp_sock_create};这里最重要的是hidp_socke_create函数,该函数中会指定socket的proto_ops。static int hidp_sock_create(struct net *net, struct socket *sock, int protocol,                     int kern)      struct sock *sk;      if (sock->type != SOCK_RAW)           return -ESOCKTNOSUPPORT;      sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto);      sock_init_data(sock, sk);      sock->ops = &hidp_sock_ops;      sock->state = SS_UNCONNECTED;      sock_reset_flag(sk, SOCK_ZAPPED);      sk->sk_protocol = protocol;      sk->sk_state     = BT_OPEN;      。。。hidp_sock_ops的定义:static const struct proto_ops hidp_sock_ops = {      .family        = PF_BLUETOOTH,      .owner        = THIS_MODULE,      .release = hidp_sock_release,      .ioctl          = hidp_sock_ioctl,      .bind          = sock_no_bind,      .getname    = sock_no_getname,      .sendmsg    = sock_no_sendmsg,      .recvmsg     = sock_no_recvmsg,      .poll           = sock_no_poll,      .listen         = sock_no_listen,      .shutdown   = sock_no_shutdown,      .setsockopt = sock_no_setsockopt,      .getsockopt = sock_no_getsockopt,      .connect     = sock_no_connect,      .socketpair  = sock_no_socketpair,      .accept       = sock_no_accept,      .mmap       = sock_no_mmap};从Hidp_sock_ops中可以看出,BTPROTO_HIDP的socket不支持通常的socket操作,比如bind、connnect、sendmsg、recvmsg等等。该socket真正支持的仅ioctl这一个操作。IOCTL中支持的命令列表如下:HIDPCONNADD         // 增加一个HID设备HIDPCONNDEL          // 删除指定的HID设备HIDPGETCONNLIST         // 得到当前所有HID设备的列表HIDPGETCONNINFO        // 得到指定HID设备的信息2.  HIDPCONNADDHIDPCONNADD命令用于添加一个HID设备,用户空间传下来的参数为结构体struct hidp_connadd_req:struct hidp_connadd_req {      int   ctrl_sock;        // control socket,类似USB中control pipe      int   intr_sock;        // interrupt socket,类似USB中int pipe      __u16 parser;      __u16 rd_size;         // report descriptor的大小      __u8 __user *rd_data; // report descriptor      __u8  country;    // 国家码,比如键盘就需要区分是美式键盘还是法式键盘      __u8  subclass;      __u16 vendor;      __u16 product;      __u16 version;      __u32 flags;      __u32 idle_to;      char  name[128];};从结构体中可以看出,该socket发送/接收外部数据都通过ctrl_sock和intr_sock完成,这两个socket由用户空间创建后传入。HIDPCONNADD的过程如下:      struct socket *csock;      struct socket *isock;      struct hidp_connadd_req ca;      。。。      csock = sockfd_lookup(ca.ctrl_sock, &err);      isock = sockfd_lookup(ca.intr_sock, &err);      hidp_add_connection(&ca, csock, isock); int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)      。。。      // 确保ctrl socket和intr socket的源地址、目的地址相同      if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) ||           bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))            return -ENOTUNIQ;      // 初始化hidp_session结构      session->ctrl_sock = ctrl_sock;      session->intr_sock = intr_sock;      session->state     = BT_CONNECTED;      setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);      skb_queue_head_init(&session->ctrl_transmit);      skb_queue_head_init(&session->intr_transmit);      。。。      // 如果有report描述符,那么创建HID设备,通过HID框架解析该描述符      if (req->rd_size > 0)           err = hidp_setup_hid(session, req);           。。。      // 如果req中没有report描述符,那么就没有创建hid设备,这时创建一个input设备。这种情况只对HID Boot Protocol Device有效,对于这种设备不需要report描述符。具体可参考HID设备的文档。由于input设备的情况比较简单,这里不再讨论if (!session->hid)           err = hidp_setup_input(session, req);           。。。      // 将session挂入hidp_session_list链表      。。。      // 建立一个内核任务      kernel_thread(hidp_session, session, CLONE_KERNEL);到这里,还剩下两个函数需要进一步分析,一个是hidp_setup_hid,另一个是hidp_session。Hidp_session比较简单:static int hidp_session(void *arg)      LOOP           // 如果ctrl socket接收到数据,则接收并处理。Ctrl通道中的数据内容建立连接、对方断开连接的通知、以及通过ctrl通道接收到的用户面数据等。           // 如果int通道接收到数据,则接收并处理。Int通道中传送用户面数据。比如鼠标移动,或者键盘按下等。如果此session使用hid设备,那么调用hid_input_report将数据发送到HID框架,由hid框架发送到用户空间程序;如果使用的是input设备,那么最终调用input_report_key等发送数据到用户空间。            // 如果控制通道有数据要发送,则发送到ctrl socket,比如主动发起的连接断开、连接建立等。           // 如果int通道有数据要发送,则发送。这里发送用户面数据,比如控制键盘上的LED灯等。 下面再分析一下hidp_setup_hid函数,此函数初始化一个HID设备并登记到HID系统。static int hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req)      struct hid_device *hid;      。。。      hid = hid_allocate_device();      session->hid = hid;      hid->driver_data = session;      。。。      // 设置该hid的父设备为所属的HCI设备      hid->dev.parent = hidp_get_device(session);      // 设置HID设备对应的物理层驱动      hid->ll_driver = &hidp_hid_driver;        // 当HID框架需要发送数据到设备时,会调用这里的hid_output_raw_report,      // 这里发送的数据会走ctrl sockethid->hid_output_raw_report = hidp_output_raw_report;      hid_add_device(hid);      。。。hidp_hid_driver是一个结构体,用来描述HID设备对应的物理层驱动。static struct hid_ll_driver hidp_hid_driver = {      // 最终会调用HID框架的hid_parse_report完成report描述符的解析.parse = hidp_parse,      // start函数比较奇怪,可能是按照HID的协议规范实现的,没有细看.start = hidp_start,// 清除缓存的待发送数据      .stop = hidp_stop,      .open  = hidp_open,     // 空函数      .close = hidp_close, // 空函数      // 这里的input_event是从本地到设备,即发送数据时调用的函数,走int socket通道      .hidinput_input_event = hidp_hidinput_event,}; 综上,当从设备到本机有数据要接收时,会在hidp_session这个内核任务中处理;当从本机到设备有数据要发送时,HID框架会调用hidp_hidinput_event或者hidp_output_raw_report,视该数据是ctrl数据还是int通道数据而定。所有数据的发送、接收都通过ctrl socket和int socket完成,而这两个socket由用户空间创建后通过调用BTPROTO_HIDP socket的ioctl HIDPCONNADD传入。

http://blog.csdn.net/walkingman321/article/details/7214427

 

转载于:https://www.cnblogs.com/MMLoveMeMM/articles/4109612.html

转载地址:https://blog.csdn.net/FLY_THINK2012/article/details/101840955 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:android sensor驱动移植
下一篇:Android Bluetooth HID实现详解

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月26日 06时27分32秒