Android f_rndis 分析笔记
来源:互联网 发布:丰田雅力士怎么样知乎 编辑:程序博客网 时间:2024/04/27 19:12
背景说明
RNDIS是一个以太网端口 ( Ethernet port )。最开始是微软控制的,用以取代 CDC Ethernet 的协议。
公开发布的 RNDIS规范很模糊,并且不必要的复杂。 ActiveSync 等规范术语使情况更糟糕。
简而言之,它是一个微软控制的,而不是开源生态系统控制的协议。 Linux 支持它仅仅是因为微软不支持 CDC以太网标准。
RNDIS数据传输模型很复杂,每个USB 消息都包含了多个以太网包。
RNDIS默认期待自己作为USB配置中的唯一功能;因此你不能把它用于USB复合设备;并且它期待自己是第一个usb配置。
很不幸,微软的RNDIS驱动程序充满了bug,经常死机或者系统冻结,且经常和规范矛盾。
对于Linux开源社区而言,既然改正这些bug, 或者从微软拿到精确的RNDIS规范文档从而绕过它都不可能,
也许你可以避免使用 RNDIS。
代码分析
kernel/drivers/usb/gadget/f_rndis.c 文件开头即定义了 f_rndis 数据结构
struct f_rndis {struct getherport;u8ctrl_id, data_id;u8ethaddr[ETH_ALEN];u32vendorID;const char*manufacturer;intconfig;struct usb_ep*notify;struct usb_request*notify_req;atomic_tnotify_count;};
随后,f_rndis.c 分别定义了各种 usb 接口 和 usb 描述符。
static struct usb_interface_descriptor rndis_control_intf = {.bLength =sizeof rndis_control_intf,.bDescriptorType =USB_DT_INTERFACE,/* status endpoint is optional; this could be patched later */.bNumEndpoints =1,.bInterfaceClass =USB_CLASS_COMM,.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,};
其后, 分别定义了 full speed, high speed, super speed的描述符。
static struct usb_descriptor_header *eth_ss_function[] = {(struct usb_descriptor_header *) &rndis_iad_descriptor,/* control interface matches ACM, not Ethernet */(struct usb_descriptor_header *) &rndis_control_intf,(struct usb_descriptor_header *) &header_desc,(struct usb_descriptor_header *) &call_mgmt_descriptor,(struct usb_descriptor_header *) &rndis_acm_descriptor,(struct usb_descriptor_header *) &rndis_union_desc,(struct usb_descriptor_header *) &ss_notify_desc,(struct usb_descriptor_header *) &ss_intr_comp_desc,/* data interface has no altsetting */(struct usb_descriptor_header *) &rndis_data_intf,(struct usb_descriptor_header *) &ss_in_desc,(struct usb_descriptor_header *) &ss_bulk_comp_desc,(struct usb_descriptor_header *) &ss_out_desc,(struct usb_descriptor_header *) &ss_bulk_comp_desc,NULL,};
前文分析过, rndis_bind_config_vendor() 是和 android usb 层沟通的桥梁函数,也是整个 f_rndis.c 文件的唯一入口函数。
rndis_bind_config_vendor()通过调用rndis_init(),向下打通了 kernel/drivers/usb/gadget/rndis.c 层。
在设置好 f_rndis 成员变量,也就是分配好必须的资源后,usb_add_function() 把该usb功能加入到配置中去。
对应 USB规范可知, 每个 usb 配置必须包含一个或多个usb功能。
intrndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],u32 vendorID, const char *manufacturer, struct eth_dev *dev){... rndis = kzalloc(sizeof *rndis, GFP_KERNEL); if (!rndis) goto fail; memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); rndis->vendorID = vendorID; rndis->manufacturer = manufacturer; rndis->port.ioport = dev; /* RNDIS activates when the host changes this filter */ rndis->port.cdc_filter = 0; /* RNDIS has special (and complex) framing */ rndis->port.header_len = sizeof(struct rndis_packet_msg_type); rndis->port.wrap = rndis_add_header; rndis->port.unwrap = rndis_rm_hdr; rndis->port.func.name = "rndis"; rndis->port.func.strings = rndis_strings; /* descriptors are per-instance copies */ rndis->port.func.bind = rndis_bind; rndis->port.func.unbind = rndis_unbind; rndis->port.func.set_alt = rndis_set_alt; rndis->port.func.setup = rndis_setup; rndis->port.func.disable = rndis_disable; status = usb_add_function(c, &rndis->port.func);......}
usb_add_function() 将调用 rndis->port.func.bind 函数并返回其值,实际就是调用 rndis_bind() 函数。
rndis_bind() 进行以太网功能驱动的初始化和绑定操作。
通过 usb_interface_id() 分配usb 未使用的接口id值。 代码中 status 应该更改为 id 提高代码可读性。
static int rndis_bind(struct usb_configuration *c, struct usb_function *f){....../* allocate instance-specific interface IDs */status = usb_interface_id(c, f);if (status < 0)goto fail;rndis->ctrl_id = status;rndis_iad_descriptor.bFirstInterface = status;rndis_control_intf.bInterfaceNumber = status;rndis_union_desc.bMasterInterface0 = status;status = usb_interface_id(c, f);if (status < 0)goto fail;rndis->data_id = status;rndis_data_intf.bInterfaceNumber = status;rndis_union_desc.bSlaveInterface0 = status;......}
usb_interface_id() 是 drivers/usb/gadget/composite.c 的通用功能函数。 根据USB 2.0规范, 每个配置最大接口数 MAX_CONFIG_INTERFACES 为16。
/* * Returns the interface ID which was allocated; or -ENODEV if no * more interface IDs can be allocated. */int usb_interface_id(struct usb_configuration *config,struct usb_function *function){unsigned id = config->next_interface_id;if (id < MAX_CONFIG_INTERFACES) {config->interface[id] = function;config->next_interface_id = id + 1;return id;}return -ENODEV;}
....../* allocate instance-specific endpoints */ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);rndis->port.in_ep = ep;ep->driver_data = cdev;/* claim */ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);rndis->port.out_ep = ep;ep->driver_data = cdev;/* claim *//* NOTE: a status/notification endpoint is, strictly speaking, * optional. We don't treat it that way though! It's simpler, * and some newer profiles don't treat it as optional. */ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);rndis->notify = ep;ep->driver_data = cdev;/* claim *//* allocate notification request and buffer */rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);rndis->notify_req->length = STATUS_BYTECOUNT;rndis->notify_req->context = rndis;rndis->notify_req->complete = rndis_response_complete;......
usb_ep_autoconfig() 函数根据给定的usb descriptor, 选择对应的 usb endpoint 值并返回。该usb端点值后面会被 usb_ep_enable()使用。
为了安全起见, 调用 usb_ep_autoconfig() 的 usb_function.bind 函数应该进行端点返回值检查以适应不同的硬件差异。因为返回值可能不是usb控制器希望的usb端点。
usb_ep_autoconfig() 定义在 kernel/drivers/usb/gadget/epautoconf.c 文件中。
usb_ep_alloc_request()函数分配并返回一个 usb_request 对象指针。usb_request 对象必须通过此函数分配,因为usb_request 对象可能
是usb控制器特定相关的初始化,或者usb端点特定相关的资源,比如 DMA描述符的分配。
usb_request 可以通过 usb_ep_queue() 提交到队列, 并且收到一个唯一的请求执行结束后的回调函数;
通过 usb_ep_free_request() 可以释放 usb_request 资源。
对于 f_rndis, usb请求执行结束后的回调函数是 rndis_response_complete()。
如果usb_request 返回状态正常,则通过 usb_ep_queue() 放入队列;否则丢掉后返回。
static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req){struct f_rndis*rndis = req->context;struct usb_composite_dev*cdev = NULL;intstatus = req->status;/* In usb plug in/out and tetherring on/off * regression tests, port.func.config *//* may be NULL pointer.*/if (rndis->port.func.config != NULL)cdev = rndis->port.func.config->cdev;elseprintk(KERN_ERR "rndis gadget driver is removed.\n");/* after TX: * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) * - RNDIS_RESPONSE_AVAILABLE (status/irq) */switch (status) {case -ECONNRESET:case -ESHUTDOWN:/* connection gone */atomic_set(&rndis->notify_count, 0);break;default:if (cdev != NULL)DBG(cdev, "RNDIS %s response error %d, %d/%d\n",ep->name, status,req->actual, req->length);/* FALLTHROUGH */case 0:if (ep != rndis->notify)break;/* handle multiple pending RNDIS_RESPONSE_AVAILABLE * notifications by resending until we're done */if (atomic_dec_and_test(&rndis->notify_count))break;status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);if (status) {atomic_dec(&rndis->notify_count);if (cdev != NULL)DBG(cdev, "notify/1 --> %d\n", status);}break;}}
回到 rndis_bind() 函数。
- 它进一步指定 fast speed, high speed, super speed的描述符;
- 注册一个全局的函数指针,指向 rndis_response_available()。rndis_response_available() 函数发送 RNDIS RESPONSE_AVAILABLE 消息到端点。
......status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,eth_ss_function);if (status)goto fail;rndis->port.open = rndis_open;rndis->port.close = rndis_close;status = rndis_register(rndis_response_available, rndis);if (status < 0)goto fail;rndis->config = status;......
rndis_setup()函数:
- 使用CDC命令封装机制来实现一个 RPC调用。
- 只检查一种 USB_DIR_OUT, 一种 USB_DIR_IN。
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)| USB_CDC_SEND_ENCAPSULATED_COMMAND:if (w_value || w_index != rndis->ctrl_id)goto invalid;/* read the request; process it later */value = w_length;req->complete = rndis_command_complete;req->context = rndis;/* later, rndis_response_available() sends a notification */break; if (w_value || w_index != rndis->ctrl_id) goto invalid; else { u8 *buf; u32 n; /* return the result */ buf = rndis_get_next_response(rndis->config, &n); if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_response_complete; req->context = rndis; rndis_free_response(rndis->config, buf); value = n; } /* else stalls ... spec says to avoid that */ } break;rndis_set_alt()函数:
- 对于控制id,调用 usb_ep_enable()
- 对于数据id, 调用 gether_connect()
rndis_disable()函数:
- 释放资源
- gether_disconnect() 断开gether连接
- 调用 usb_ep_disable() 关闭端点。
从上面分析可知, f_rndis.c 沟通的下层是 drivers/usb/gadget/u_ether.c。
- Android f_rndis 分析笔记
- Android f_rndis 分析笔记
- android学习笔记分析
- android usb 分析笔记
- Android Linker分析笔记
- android 功耗分析琐碎笔记
- Android内存泄漏分析笔记
- Android 按键流程分析笔记
- Android studio工程分析笔记
- 《Android软件安全与逆向分析》笔记
- Android学习笔记之项目清单分析
- Android学习笔记之ProgressBar案例分析
- [笔记] Android Handler leak 分析及解决办法
- Android 启动过程分析--笔记缩减
- Android zygote分析——学习笔记
- Android系统源代码情景分析笔记00
- Andriod群英传-Android Scroll 分析学习笔记
- Android逆向笔记之smali代码分析
- 十年经济会议定调
- 漫画一组
- Android自动化测试中uiautomator修改uiautomatorviewer获取不到动态界面的缺陷
- 老妈的腰扭伤了
- [ZT]千兆光纤 GBIC和SFP接口规格介绍
- Android f_rndis 分析笔记
- 几种光纤接口(ST,SC,LC,FC)
- 无论你在哪里我都是守护你的翅膀
- 中国经济的长周期走势
- 女儿,你好历害!
- 终于看了传说中的<娲居>
- 在cocos2dx3.0 lua教程 自己写的类连接lua 使lua能调用自己类的函数
- 【jquery】jquery中开发插件的两个方法jquery.fn.extend与jquery.extend(推荐)
- 2009,即将成为过去!