A problem caused by incomplete implementation of USB rndis network card driver on Linux host

bigfish99 2021-06-10 07:39:48
problem caused incomplete implementation usb


A communication module equipment , adopt USB Provide RDNIS and ECM Network card function . It is found in practical application that ,USB RNDIS In NIC mode , When using AT Instructions are given in different ways CID When dialing , stay Windows The host can dial successfully , But in Linux The dialing failure will happen on the host . As a contrast , In the same test environment and test method ,USB ECM The network card has no such exception .

 

The test process is summarized as follows :

- The device side has been configured to USB RNDIS Pattern

- The host side passes through AT Instruction to CID1 Dialing succeeded

-  Test network functions , The host and device side can ping through

- The host side passes through AT Command off CID1 dial

-  The host side passes through AT Instruction to CID2 Dialing failed , The host and device side cannot ping through

 

This is the background of the problem .

 

USB ECM yes USB-IF Defined CDC A subclass specification under the class specification , Full name Ethernet Networking Control Model;RNDIS It's a specification developed by Microsoft for plug and play Ethernet devices . The implementation of these two protocols USB equipment , adopt USB After the cable is connected to the host , A network card will be generated on the host side and the device side respectively . The network cards on both sides are in the same network segment , Network communication , The data bearer path is USB. Here's an excerpt from Microsoft's website RNDIS Frame diagram :

 

https://docs.microsoft.com/en-us/windows-hardware/drivers/network/overview-of-remote-ndis--rndis-

 

 

After investigation ,Linux On a host RNDIS Dial up test failed , The main reason is : When the first dial is successful , When dialing off ,Linux Host computer USB network card IP The address didn't disappear , The follow-up is different CID After dialing ,Linux And there was no initiation DHCP Request package ,DHCP Process failed ,IP Address not updated . here , If you will USB Just plug it in and it's back to normal .

adopt AT When the instruction tells the device to disconnect dialing , There will be down USB Action of network card ,down USB In the process of network card , Equipment side RNDIS Will report rndis disconnect Message to inform the host side . The host side can process the message accordingly .

 

stay Ubuntu The host and Windows When testing the disconnect dial operation on the host , Grab it on the device side kernel log The snippet is as follows . You can see , Whether in the Windows It's still on the mainframe Ubuntu On a host , The device side did report when the dialing was disconnected rndis disconnect news .

Ubuntu Host environment , Device end log:
root@udx710-module:~#
[ 324.516525] c0 configfs-gadget gadget: rndis_close
[ 324.521239] c0 rndis_set_param_medium: 0 0
[ 324.525296] c0 rndis_signal_disconnect
[ 324.529023] c0 rndis_indicate_status_msg: status 1073807372
​
Windows Host environment , Device end log:
root@udx710-module:~#
[ 191.340507] c1 configfs-gadget gadget: rndis_close
[ 191.345223] c1 rndis_set_param_medium: 0 0
[ 191.349285] c1 rndis_signal_disconnect
[ 191.353008] c1 rndis_indicate_status_msg: status 1073807372
[ 191.364621] c1 configfs-gadget gadget: rndis reqa1.01 v0000 i0000 l4096

 

Device side action

The key codes on the device side are as follows :

rndis_close -> rndis_signal_disconnect -> rndis_indicate_status_msg

drivers/usb/gadget/function/rndis.c
​
int rndis_signal_disconnect(struct rndis_params *params)
{
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT);
}
​
/*
* Device to Host Comunication
*/
static int rndis_indicate_status_msg(struct rndis_params *params, u32 status)
{
rndis_indicate_status_msg_type *resp;
rndis_resp_t *r;
​
if (params->state == RNDIS_UNINITIALIZED)
return -ENOTSUPP;
​
r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type));
if (!r)
return -ENOMEM;
resp = (rndis_indicate_status_msg_type *)r->buf;
​
resp->MessageType = cpu_to_le32(RNDIS_MSG_INDICATE);
resp->MessageLength = cpu_to_le32(20);
resp->Status = cpu_to_le32(status);
resp->StatusBufferLength = cpu_to_le32(0);
resp->StatusBufferOffset = cpu_to_le32(0);
​
params->resp_avail(params->v);
return 0;
}
​
drivers/usb/gadget/function/f_rndis.c
​
static void rndis_response_available(void *_rndis)
{
struct f_rndis *rndis = _rndis;
struct usb_request *req = rndis->notify_req;
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
__le32 *data = req->buf;
int status;
​
if (atomic_inc_return(&rndis->notify_count) != 1)
return;
​
/* Send RNDIS RESPONSE_AVAILABLE notification; a
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
*
* This is the only notification defined by RNDIS.
*/
data[0] = cpu_to_le32(1);
data[1] = cpu_to_le32(0);
​
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
if (status) {
atomic_dec(&rndis->notify_count);
DBG(cdev, "notify/0 --> %d\n", status);
}
}

rndis_indicate_status_msg  Can do two things :

1) hold RNDIS_MSG_INDICATE message Put in response queue,message With status(RNDIS_STATUS_MEDIA_DISCONNECT).

2) call resp_avail Hair RESPONSE_AVAILABLE notification( Finally call to rndis_response_available @ f_rndis.c), adopt interrupt IN Send it out , This data doesn't contain rndis Connection status information , It's just a report RESPONSE_AVAILABLE (0x00000001).

 

Host side action

1) According to Microsoft's RNDIS standard ,Host received RESPONSE_AVAILABLE after , Next, we need to go to control Endpoint sending GET_ENCAPSULATED_RESPONSE request To read the response, obtain rndis Connection status .

Upon receiving the RESPONSE_AVAILABLE notification, the host reads the control message from the Control endpoint using a GET_ENCAPSULATED_RESPONSE transfer, defined in the following table.

BmRequestType bRequest wValue wIndex    
0xA1 0x01 0x0000      
https://docs.microsoft.com/en-us/windows-hardware/drivers/network/control-channel-characteristics

 

2) Look again rndis host drive :

rndis_status @ rndis_host.c The function doesn't do the actual action .

drivers/net/usb/rndis_host.c
void rndis_status(struct usbnet *dev, struct urb *urb)
{
netdev_dbg(dev->net, "rndis status urb, len %d stat %d\n",
urb->actual_length, urb->status);
// FIXME for keepalives, respond immediately (asynchronously)
// if not an RNDIS status, do like cdc_status(dev,urb) does
}
​
static const struct driver_info rndis_info = {
.description = "RNDIS device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
.bind = rndis_bind,
.unbind = rndis_unbind,
.status = rndis_status,
.rx_fixup = rndis_rx_fixup,
.tx_fixup = rndis_tx_fixup,
};

in other words ,Linux host RNDIS The drive is not strictly in accordance with RNDIS Protocol flow to read RNDIS_STATUS_MEDIA_DISCONNECT news , So it can't know the device side RNDIS The network card is disconnected , And then can not make the correct network state change related processing .

 

And the author also expressed his attitude in the code comments : It is strongly recommended not to use RNDIS, And should be used CDC Ethernet (ECM,NCM,EEM etc. ) This kind of non exclusive (non-proprietary) alternatives .USB CDC The norm is USB-IF To formulate the ,RNDIS It's Microsoft .

/*
* RNDIS is NDIS remoted over USB. It's a MSFT variant of CDC ACM ... of
* course ACM was intended for modems, not Ethernet links! USB's standard
* for Ethernet links is "CDC Ethernet", which is significantly simpler.
*
* NOTE that Microsoft's "RNDIS 1.0" specification is incomplete. Issues
* include:
* - Power management in particular relies on information that's scattered
* through other documentation, and which is incomplete or incorrect even
* there.
* - There are various undocumented protocol requirements, such as the
* need to send unused garbage in control-OUT messages.
* - In some cases, MS-Windows will emit undocumented requests; this
* matters more to peripheral implementations than host ones.
*
* Moreover there's a no-open-specs variant of RNDIS called "ActiveSync".
*
* For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in
* favor of such non-proprietary alternatives as CDC Ethernet or the newer (and
* currently rare) "Ethernet Emulation Model" (EEM).
*/

Finally, by the way, see why ECM No problem .

 

ECM Equipment side ,ecm_close -> ecm_notify -> ecm_do_notify -> adopt interrupt IN Send it out , This data directly brings ecm Connection status , There is no need to Host Send another one request Read this state .

static void ecm_do_notify(struct f_ecm *ecm)
{
struct usb_request *req = ecm->notify_req;
struct usb_cdc_notification *event;
...
event = req->buf;
switch (ecm->notify_state) {
...
case ECM_NOTIFY_CONNECT:
event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
if (ecm->is_open)
event->wValue = cpu_to_le16(1);
else
event->wValue = cpu_to_le16(0);
event->wLength = 0;
req->length = sizeof *event;
​
DBG(cdev, "notify connect %s\n",
ecm->is_open ? "true" : "false");
ecm->notify_state = ECM_NOTIFY_SPEED;
break;
...
}

ECM Main engine side ,usbnet_cdc_status @ cdc_ether.c There is processing in the function ecm connection Message and call usbnet_link_change.

void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
{
struct usb_cdc_notification *event;
...
event = urb->transfer_buffer;
switch (event->bNotificationType) {
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
event->wValue ? "on" : "off");
usbnet_link_change(dev, !!event->wValue, 0);
break;
...
}
}
​
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT,
.bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = usbnet_cdc_status,
.set_rx_mode = usbnet_cdc_update_filter,
.manage_power = usbnet_manage_power,
};

 

copyright , Reprint please indicate the source .

The article will be synchronized to “ Big fish ”, Welcome to your attention , Communicate together .

版权声明
本文为[bigfish99]所创,转载请带上原文链接,感谢
https://javamana.com/2021/05/20210522103911789e.html

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云