USB NCM usbnet 枚举流程代码分析

  • 一、cdc_ncm.c
    • 1.1 【id_table】struct usb_device_id结构体
      • 1.1.1 match_flags 设备类型
      • 1.1.2 bInterfaceClass 接口类
    • 1.2 【usbnet_probe】NCM 驱动探测函数

一、cdc_ncm.c

在linux 驱动中,CDC NCM的驱动位于: kernel\drivers\net\usb\cdc_ncm.c

static struct usb_driver cdc_ncm_driver = {.name = "cdc_ncm",.id_table = cdc_devs,.probe = usbnet_probe,.disconnect = usbnet_disconnect,.suspend = usbnet_suspend,.resume = usbnet_resume,.reset_resume =  usbnet_resume,.supports_autosuspend = 1,.disable_hub_initiated_lpm = 1,
};module_usb_driver(cdc_ncm_driver);

在结构体cdc_ncm_driver中,比较重要的是id_tableprobe这两个,
id_table 中定义了所有符合cdc_ncm设备的 idVendor号,当linux 枚举时检测到USB设备的idVendor在这个id_table中时,就会调用usbnet_probe进行起网操作。

1.1 【id_table】struct usb_device_id结构体

在分析cdc_ncmid_table之前,我们先来看下usb_device_id 结构体是如何定义的:

# kernel\include\linux\mod_devicetable.h
struct usb_device_id {/* which fields to match against? */__u16     match_flags;            //说明使用哪种匹配方式/* Used for product specific matches; range is inclusive */__u16        idVendor;               //供应商ID  __u16      idProduct;              //产品ID __u16        bcdDevice_lo;__u16      bcdDevice_hi;/* Used for device class matches */__u8        bDeviceClass;           //设备类型  __u8        bDeviceSubClass;        //设备子类型 __u8        bDeviceProtocol;        //协议 /* Used for interface class matches */__u8     bInterfaceClass;        //接口类 __u8      bInterfaceSubClass;     //接口子类  __u8        bInterfaceProtocol;     //接口协议 /* Used for vendor-specific interface matches */__u8     bInterfaceNumber;/* not matched against */kernel_ulong_t    driver_info__attribute__((aligned(sizeof(kernel_ulong_t))));    // NCM 驱动结构体,其中包含了收发包的函数等
};

结合结构体定义,我们来看下cdc_ncmid_table

static const struct usb_device_id cdc_devs[] = {// 爱立信 F5521gw USB网卡/* Ericsson MBM devices like F5521gw */{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_VENDOR, //匹配方式:CLASS(设备类)、SUBCLASS(子类)、PROTOCOL(设备协议).idVendor = 0x0bdb,                                                      //供应商ID: 0x0bdb.bInterfaceClass = USB_CLASS_COMM,                                       //设备类型: 网卡类.bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,                              //设备子类型:NCM.bInterfaceProtocol = USB_CDC_PROTO_NONE,                                    //设备协议:  无.driver_info = (unsigned long) &wwan_info,                               // NCM 驱动结构体,其中包含了收发包的函数等},/* DW5812 LTE Verizon Mobile Broadband Card Unlike DW5550 this device requires FLAG_NOARP */{ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bb,       //匹配方式:USB_DEVICE_ID_MATCH_INT_INFO、USB_DEVICE_ID_MATCH_DEVICEUSB_CLASS_COMM,USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),.driver_info = (unsigned long)&wwan_noarp_info,},/* DW5813 LTE AT&T Mobile Broadband Card Unlike DW5550 this device requires FLAG_NOARP */{ USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bc,USB_CLASS_COMM,USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),.driver_info = (unsigned long)&wwan_noarp_info,},/* Dell branded MBM devices like DW5550 */{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_VENDOR,.idVendor = 0x413c,.bInterfaceClass = USB_CLASS_COMM,.bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,.bInterfaceProtocol = USB_CDC_PROTO_NONE,.driver_info = (unsigned long) &wwan_info,},/* Toshiba branded MBM devices */{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_VENDOR,.idVendor = 0x0930,.bInterfaceClass = USB_CLASS_COMM,.bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,.bInterfaceProtocol = USB_CDC_PROTO_NONE,.driver_info = (unsigned long) &wwan_info,},/* tag Huawei devices as wwan */{ USB_VENDOR_AND_INTERFACE_INFO(0x12d1,USB_CLASS_COMM,USB_CDC_SUBCLASS_NCM,USB_CDC_PROTO_NONE),.driver_info = (unsigned long)&wwan_info,},/* Infineon(now Intel) HSPA Modem platform */{ USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443,USB_CLASS_COMM,USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),.driver_info = (unsigned long)&wwan_noarp_info,},/* Generic CDC-NCM devices */{ USB_INTERFACE_INFO(USB_CLASS_COMM,USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),.driver_info = (unsigned long)&cdc_ncm_info,}
};
MODULE_DEVICE_TABLE(usb, cdc_devs);

初始化 usb_device_id 结构体时,除了可以使用上面每个成员赋值的方式,还可以使用如下宏控更加快捷的初始化:

USB_DEVICE(vendor, product)
创建一个 struct usb_device_id, 可用来只匹配特定供应商和产品 ID 值,
对于需要特定驱动的 USB 设备,这是非常普遍用的。USB_DEVICE_VER(vendor, product, lo, hi)
创建一个 struct usb_device_id, 用来在一个版本范围中只匹配特定供应商和产品 ID 值。USB_DEVICE_INFO(class, subclass, protocol)
创建一个 struct usb_device_id, 可用来只匹配一个特定类的 USB 设备。USB_INTERFACE_INFO(class, subclass, protocol)
创建一个 struct usb_device_id, 可用来只匹配一个特定类的 USB 接口。

1.1.1 match_flags 设备类型

所有的match_flags定义在mod_devicetable.h 中:

# kernel\include\linux\mod_devicetable.h
/* Some useful macros to use to create struct usb_device_id */
#define USB_DEVICE_ID_MATCH_VENDOR          0x0001
#define USB_DEVICE_ID_MATCH_PRODUCT         0x0002
#define USB_DEVICE_ID_MATCH_DEV_LO          0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI          0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS       0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS    0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL    0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS       0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS    0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL    0x0200
#define USB_DEVICE_ID_MATCH_INT_NUMBER      0x0400

1.1.2 bInterfaceClass 接口类

# kernel\include\uapi\linux\usb\ch9.h/* Device and/or Interface Class codes* as found in bDeviceClass or bInterfaceClass* and defined by www.usb.org documents */
#define USB_CLASS_PER_INTERFACE     0   /* for DeviceClass */
#define USB_CLASS_AUDIO         1                   //声音设备
#define USB_CLASS_COMM          2                   //调制解调器,网卡,ISDN连接
#define USB_CLASS_HID           3                   //HID设备,如鼠标,键盘
#define USB_CLASS_PHYSICAL      5                   //物理设备
#define USB_CLASS_STILL_IMAGE   6                   //静止图像捕捉设备
#define USB_CLASS_PRINTER       7                   //打印机
#define USB_CLASS_MASS_STORAGE  8                   //批量存储设备
#define USB_CLASS_HUB           9                   //USB HUBS
#define USB_CLASS_CDC_DATA      0x0a
#define USB_CLASS_CSCID         0x0b    /* chip+ smart card */     //智能卡
#define USB_CLASS_CONTENT_SEC       0x0d    /* content security */
#define USB_CLASS_VIDEO         0x0e                //视频设备,如网络摄像头
#define USB_CLASS_WIRELESS_CONTROLLER   0xe0        //网络控制设备
#define USB_CLASS_MISC          0xef                //杂项设备
#define USB_CLASS_APP_SPEC      0xfe
#define USB_CLASS_VENDOR_SPEC       0xff            //厂商自定义的设备#define USB_SUBCLASS_VENDOR_SPEC  0xff

1.2 【usbnet_probe】NCM 驱动探测函数

注册ncm 驱动使用的是module_usb_driver(cdc_ncm_driver);
但因为是通用usb驱动代码,所以这个不是我们要关心的,接下来,我们要关心的是当注册好ncm usb驱动后,
系统通过bInterfaceSubClass 检测到ncm 设备接入时,同时根据match_flags匹配成功时,就会调用用预先配置好的probe 函数进行ncm设备初始化操作。

主要工作如下:

  1. 初始化struct net_device结构体,结构体中包含了网络操作相关的参数及操作函数
# kernel\drivers\net\usb\usbnet.c
int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{struct usbnet          *dev;struct net_device      *net;struct usb_host_interface  *interface;struct driver_info       *info;struct usb_device     *xdev;int               status;const char           *name;struct usb_driver     *driver = to_usb_driver(udev->dev.driver);name = udev->dev.driver->name;info = (struct driver_info *) prod->driver_info;xdev = interface_to_usbdev (udev);interface = udev->cur_altsetting;status = -ENOMEM;// 1. 初始化net_device结构体,结构体中包含了网络操作相关的参数及操作函数// set up our own recordsnet = alloc_etherdev(sizeof(*dev));// 将udev 设置为net的sys文件系统的 parent/* netdev_printk() needs this so do it as early as possible */SET_NETDEV_DEV(net, &udev->dev);// 2. 初始化各种queue队列,包括,等待队列,接收队列,发送队列。dev = netdev_priv(net);dev->udev = xdev;dev->intf = udev;dev->driver_info = info;dev->driver_name = name;dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK);init_waitqueue_head(&dev->wait);skb_queue_head_init (&dev->rxq);skb_queue_head_init (&dev->txq);skb_queue_head_init (&dev->done);skb_queue_head_init(&dev->rxq_pause);dev->bh.func = usbnet_bh;dev->bh.data = (unsigned long) dev;INIT_WORK (&dev->kevent, usbnet_deferred_kevent);init_usb_anchor(&dev->deferred);dev->delay.function = usbnet_bh;dev->delay.data = (unsigned long) dev;init_timer (&dev->delay);mutex_init (&dev->phy_mutex);mutex_init(&dev->interrupt_mutex);dev->interrupt_count = 0;dev->net = net;strcpy (net->name, "usb%d");memcpy (net->dev_addr, node_id, sizeof node_id);/* rx and tx sides can use different message sizes;* bind() should set rx_urb_size in that case.*/dev->hard_mtu = net->mtu + net->hard_header_len;net->netdev_ops = &usbnet_netdev_ops;net->watchdog_timeo = TX_TIMEOUT_JIFFIES;net->ethtool_ops = &usbnet_ethtool_ops;// allow device-specific bind/init procedures// NOTE net->name still not usable ...if (info->bind) {status = info->bind (dev, udev);if (status < 0)goto out1;// heuristic:  "usb%d" for links we know are two-host,// else "eth%d" when there's reasonable doubt.  userspace// can rename the link if it knows better.if ((dev->driver_info->flags & FLAG_ETHER) != 0 &&((dev->driver_info->flags & FLAG_POINTTOPOINT) == 0 ||(net->dev_addr [0] & 0x02) == 0))strcpy (net->name, "eth%d");/* WLAN devices should always be named "wlan%d" */if ((dev->driver_info->flags & FLAG_WLAN) != 0)strcpy(net->name, "wlan%d");/* WWAN devices should always be named "wwan%d" */if ((dev->driver_info->flags & FLAG_WWAN) != 0)strcpy(net->name, "wwan%d");/* LTE devices should always be named "lte%d" */if ((dev->driver_info->flags & FLAG_LTE) != 0)strcpy(net->name, "lte%d");/* devices that cannot do ARP */if ((dev->driver_info->flags & FLAG_NOARP) != 0)net->flags |= IFF_NOARP;/* maybe the remote can't receive an Ethernet MTU */if (net->mtu > (dev->hard_mtu - net->hard_header_len))net->mtu = dev->hard_mtu - net->hard_header_len;} else if (!info->in || !info->out)status = usbnet_get_endpoints (dev, udev);else {dev->in = usb_rcvbulkpipe (xdev, info->in);dev->out = usb_sndbulkpipe (xdev, info->out);if (!(info->flags & FLAG_NO_SETINT))status = usb_set_interface (xdev,interface->desc.bInterfaceNumber,interface->desc.bAlternateSetting);elsestatus = 0;}if (status >= 0 && dev->status)status = init_status (dev, udev);if (status < 0)goto out3;if (!dev->rx_urb_size)dev->rx_urb_size = dev->hard_mtu;dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);/* let userspace know we have a random address */if (ether_addr_equal(net->dev_addr, node_id))net->addr_assign_type = NET_ADDR_RANDOM;if ((dev->driver_info->flags & FLAG_WLAN) != 0)SET_NETDEV_DEVTYPE(net, &wlan_type);if ((dev->driver_info->flags & FLAG_WWAN) != 0)SET_NETDEV_DEVTYPE(net, &wwan_type);/* initialize max rx_qlen and tx_qlen */usbnet_update_max_qlen(dev);if (dev->can_dma_sg && !(info->flags & FLAG_SEND_ZLP) &&!(info->flags & FLAG_MULTI_PACKET)) {dev->padding_pkt = kzalloc(1, GFP_KERNEL);if (!dev->padding_pkt) {status = -ENOMEM;goto out4;}}status = register_netdev (net);if (status)goto out5;netif_info(dev, probe, dev->net,"register '%s' at usb-%s-%s, %s, %pM\n",udev->dev.driver->name,xdev->bus->bus_name, xdev->devpath,dev->driver_info->description,net->dev_addr);// ok, it's ready to go.usb_set_intfdata (udev, dev);netif_device_attach (net);if (dev->driver_info->flags & FLAG_LINK_INTR)usbnet_link_change(dev, 0, 0);return 0;out5:kfree(dev->padding_pkt);
out4:usb_free_urb(dev->interrupt);
out3:if (info->unbind)info->unbind (dev, udev);
out1:/* subdrivers must undo all they did in bind() if they* fail it, but we may fail later and a deferred kevent* may trigger an error resubmitting itself and, worse,* schedule a timer. So we kill it all just in case.*/cancel_work_sync(&dev->kevent);del_timer_sync(&dev->delay);free_netdev(net);
out:return status;
}
EXPORT_SYMBOL_GPL(usbnet_probe);

USB NCM usbnet 枚举流程代码分析相关推荐

  1. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  2. 【鸿蒙OS开发入门】13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解

    [鸿蒙OS开发入门]13 - 启动流程代码分析之第一个用户态进程:init 进程 之 init 任务详解 一. /etc/init.cfg 系统默认cfg:启动lo回环网卡 1.1 init.Hi35 ...

  3. 【鸿蒙OS开发入门】06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 启动init进程

    [鸿蒙OS开发入门]06 - 启动流程代码分析之KernelOS:之启动Linux-4.19 Kernel内核 一.head.S 启动start_kernel() 1.1 start_kernel() ...

  4. MSM USB插入流程代码分析

    点击打开链接 代码路径:kernel\msm-3.18\drivers\power\qpnp-smbcharger.c src_detect_handler -->update_usb_stat ...

  5. PCIe学习笔记之pcie初始化枚举和资源分配流程代码分析

    本文主要是对PCIe的初始化枚举.资源分配流程进行分析.代码对应的是linux 4.19, 平台是arm64. 文章首发于这里 1. PCIe architecture 1.1 pcie的拓扑结构 在 ...

  6. 【ceph】ceph命令处理流程代码分析(终端敲命令之后发生的事)

    目录 后端部分 Admin Socket 前端部分 python ceph 命令下发流程:命令行/usr/bin/ceph-->python 前端部分处理-->Admin Socket后端 ...

  7. 【OpenCV流程+代码分析】Opencv HOG行人检测 源码分析

    [原文:http://blog.csdn.net/ttransposition/article/details/11874285] OpenCV demo演示见本人的另一篇灌水博客 http://bl ...

  8. 【Heritrix基础教程之4】开始一个爬虫抓取的全流程代码分析

    在创建一个job后,就要开始job的运行,运行的全流程如下: 1.在界面上启动job 2.index.jsp 查看上述页面对应的源代码 <a href='"+request.getCo ...

  9. 【Android SDM660源码分析】- 04 - UEFI ABL LinuxLoader 代码分析

    [Android SDM660源码分析]- 04 - UEFI ABL LinuxLoader 代码分析 1. LinuxLoader.c 系列文章: <[Android SDM660开机流程] ...

最新文章

  1. 中小型金融企业该如何进行灾备建设?
  2. log4j2的xml的配置样例
  3. JavaScript基础一
  4. mysql保存中文乱码的原因和解决办法
  5. java递归基本结构_JAVA学习入门篇_递归结构
  6. 安装 ibm-java-x86_64-sdk-6.0-9.3.x86_64.rpm 的三步骤
  7. LINQ之路 5:LINQ查询表达式
  8. #279. [SYZOI Round1] 滑稽♂树(树状数组套主席树)
  9. 使用C++代码打印数字正方形
  10. Linux命令整理 —— 目录结构
  11. mongodb更新操作符$min,$max
  12. 《spring-boot学习》-09-spring boot+mybatis多数据源
  13. 类的static成员并用其实现一个单例模式
  14. php用curl调用接口方法,get和post两种方式
  15. OmniPlan Pro 4 for Mac(项目流程管理)
  16. ensp下载最新版及所有资源包免费分享下载
  17. 电脑锁屏 快捷方式设置
  18. 防火墙之firewalld
  19. 【夜读】一个人最积极的生活状态
  20. 【17 提高 1】 给

热门文章

  1. matlab 批量修改文件名常见错误
  2. 厉害了,可以不重启JVM就替换掉已经加载的类?
  3. Could not transfer artifact XXX:XXX:pom:XX from/to镜像地址
  4. cups共享linux打印机_利用CUPS为linux安装打印服务并局域网共享
  5. 网易患病员工被保安赶出公司,项目经理该如何应对中年危机?
  6. .net学习笔记——学生信息管理系统(一、在SQL server中新建学生信息库)
  7. Notification和NotificationManagerService原理解析
  8. 《庖丁解牛Linux内核》笔记之:汇编基础
  9. java实现文本纠错功能_调用百度API进行文本纠错
  10. Linux权限drwxrwxrwx是什么意思?