1. 前言
RNDIS: Remote Network Driver Interface Specification,既是RemoteNDIS,既是远程网络驱动接口规范。基于USB实现RNDIS实际上就是TCP/IP over USB,就是在USB设备上跑TCP/IP,让USB设备看上去像一块网卡。
cdc_ether驱动在枚举时相对usb-serial要简单的多,因为usb-serial驱动在枚举时涉及到两种总线,即usb和usb-serial总线,所以注册会比较麻烦,而cdc_ether只在usb总线上注册。
CDC: 通讯设备类
ECM: 以太网控制模型
EEM: 以太网仿真模型
本文主要讲解cdc_ether驱动!
2. 流程图
2.1 cdc_ether驱动注册到usb总线上流程图
2.2 cdc_ether驱动usb枚举过程
2.3 cdc_ether usb网络操作过程
3. cdc_ether驱动注册
3.1 cdc驱动结构体定义
static struct usb_driver cdc_driver = {
.name = "cdc_ether",
.id_table = products, //这里有个产品很重要,见下面
.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_driver);
cdc驱动最终会通过cdc_driver驱动的这个部分匹配
.id_table = products,
在其内部将调用cdc,如下
static const struct usb_device_id products [] = {
......
{
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,USB_CDC_PROTO_NONE),
.driver_info = (unsigned long) &cdc_info,
}
......
}
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT, //eth%d或者usb%d
// .check_connect = cdc_check_connect,
.bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = usbnet_cdc_status,
.manage_power = usbnet_manage_power,
};
如果cdc驱动最终probe成功,将调用该结构体的usbnet_cdc_bind驱动,这个先不分析,后面在讲解....
路径:linux-3.10.x\include\linux\usb.h
#define module_usb_driver(__usb_driver) \
module_driver(__usb_driver, usb_register, \
usb_deregister)
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
到这里又见到了我们熟悉的usb注册驱动接口
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
//对drvwrap USB驱动包初始化
new_driver->drvwrap.for_devices = 0;
new_driver->drvwrap.driver.name = (char *) new_driver->name; //初始化驱动名称为“cdc_ether”
new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定到usb总线上
new_driver->drvwrap.driver.probe = usb_probe_interface; //重要,是接口的探针函数!!!该函数内部会调用usbnet_probe()
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver); //驱动注册,即在usb总线上注册cdc_ether驱动
if (retval)
goto out;
retval = usb_create_newid_files(new_driver);
if (retval)
goto out_newid;
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
out:
return retval;
out_newid:
driver_unregister(&new_driver->drvwrap.driver);
printk(KERN_ERR "%s: error %d registering interface "
" driver %s\n",
usbcore_name, retval, new_driver->name);
goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
这里最终调用driver_register()注册cdc_ether驱动到usb总线上,driver_register()函数内部的细节这里不在赘述,它里面调用bus_add_driver(),而bus_add_driver我在之前的博客里有多次提到,可以参照:点击打开链接
至此就完成了cdc_ether驱动注册到usb总线上的过程,分过这里还有一个任务没有分析,就是static struct usb_driver cdc_driver 驱动具体的成员功能...
4. cdc_ether驱动枚举过程
堆栈信息:
Backtrace:
[<c0012df8>] (dump_backtrace+0x0/0x10c) from [<c0012f1c>] (show_stack+0x18/0x1c)
r7:c035d374 r6:c28d75e0 r5:00000000 r4:c2b220c8
[<c0012f04>] (show_stack+0x0/0x1c) from [<c03244b4>] (dump_stack+0x20/0x2c)
[<c0324494>] (dump_stack+0x0/0x2c) from [<c02399e8>] (usbnet_probe+0x578/0x63c)
[<c0239470>] (usbnet_probe+0x0/0x63c) from [<c02486f4>] (usb_probe_interface+0xf0/0x1d8)
[<c0248604>] (usb_probe_interface+0x0/0x1d8) from [<c01f69e4>] (driver_probe_device+0x9c/0x238)
[<c01f6948>] (driver_probe_device+0x0/0x238) from [<c01f6c5c>] (__device_attach+0x44/0x48)
[<c01f6c18>] (__device_attach+0x0/0x48) from [<c01f4ff8>] (bus_for_each_drv+0x68/0x94)
r5:c38adc10 r4:00000000
[<c01f4f90>] (bus_for_each_drv+0x0/0x94) from [<c01f6d14>] (device_attach+0x88/0xa0)
r7:c28a2a0c r6:c2873d54 r5:c0474cf8 r4:c2873d20
[<c01f6c8c>] (device_attach+0x0/0xa0) from [<c01f5cc0>] (bus_probe_device+0x8c/0xb4)
r7:c28a2a0c r6:c2873d20 r5:c0474cf8 r4:c04b8378
[<c01f5c34>] (bus_probe_device+0x0/0xb4) from [<c01f45d0>] (device_add+0x310/0x648)
r7:c28a2a0c r6:00000003 r5:c2873d20 r4:c04b8378
[<c01f42c0>] (device_add+0x0/0x648) from [<c02475b8>] (usb_set_configuration+0x444/0x820)
[<c0247174>] (usb_set_configuration+0x0/0x820) from [<c0250114>] (generic_probe+0x34/0x98)
[<c02500e0>] (generic_probe+0x0/0x98) from [<c02480cc>] (usb_probe_device+0x38/0x48)
r5:c0475280 r4:c2a0ec68
[<c0248094>] (usb_probe_device+0x0/0x48) from [<c01f69e4>] (driver_probe_device+0x9c/0x238)
r5:c2a0ec68 r4:c0475294
[<c01f6948>] (driver_probe_device+0x0/0x238) from [<c01f6c5c>] (__device_attach+0x44/0x48)
[<c01f6c18>] (__device_attach+0x0/0x48) from [<c01f4ff8>] (bus_for_each_drv+0x68/0x94)
r5:c38addb0 r4:00000000
[<c01f4f90>] (bus_for_each_drv+0x0/0x94) from [<c01f6d14>] (device_attach+0x88/0xa0)
r7:00000001 r6:c2a0ec9c r5:c0474cf8 r4:c2a0ec68
[<c01f6c8c>] (device_attach+0x0/0xa0) from [<c01f5cc0>] (bus_probe_device+0x8c/0xb4)
r7:00000001 r6:c2a0ec68 r5:c0474cf8 r4:c38ade29
[<c01f5c34>] (bus_probe_device+0x0/0xb4) from [<c01f45d0>] (device_add+0x310/0x648)
r7:00000001 r6:c3865080 r5:c2a0ec68 r4:c38ade29
[<c01f42c0>] (device_add+0x0/0x648) from [<c023ef64>] (usb_new_device+0x1f8/0x378)
[<c023ed6c>] (usb_new_device+0x0/0x378) from [<c0240de0>] (hub_thread+0xe30/0x15a0)
[<c023ffb0>] (hub_thread+0x0/0x15a0) from [<c003ef28>] (kthread+0xb0/0xbc)
[<c003ee78>] (kthread+0x0/0xbc) from [<c000f850>] (ret_from_fork+0x14/0x24)
r8:00000000 r7:00000000 r6:00000000 r5:c003ee78 r4:c3843e98
usb 1-1: adding 1-1:1.4 (config #1, interface 4)
hub 1-0:1.0: state 7 ports 2 chg 0000 evt 0002
nuc970-ehci nuc970-ehci: GetStatus port:1 status 00100d 0 ACK POWER sig=se0 PEC PE CONNECT
hub 1-0:1.0: port 1 enable change, status 00000503
usb 1-1: link qh32-0001/c2a847c0 start 1 [1/0 us]
usb 1-1: unlink qh32-0001/c2a847c0 start 1 [1/0 us]
usb 1-1: link qh32-0001/c2aca080 start 2 [1/0 us]
通过上面usbnet的枚举信息,得知最终枚举的usb设备会调用cdc_ether驱动的usbnet_probe探测函数,为什么会调用它呢?其实是usb在枚举后会创建设备,该设备注册到的也是在usb总线上,同时在该设备枚举时也获取了usb接口上物理设备(如这里是MODEM)的厂商 VENDOR ID和产品Product ID,这样将枚举的设备与在USB总线上的驱动(.id_table =
products)通过Vendor ID 和 Product ID进行匹配,成功就调用该驱动对应的探测函数,这里就是usbnet_probe()。
5. usbnet_probe()
int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{
struct usbnet *dev; //usb core核心层网络
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);
/* usbnet already took usb runtime pm, so have to enable the feature
* for usb interface, otherwise usb_autopm_get_interface may return
* failure if RUNTIME_PM is enabled.
*/
if (!driver->supports_autosuspend) {
driver->supports_autosuspend = 1;
pm_runtime_enable(&udev->dev);
}
name = udev->dev.driver->name;
info = (struct driver_info *) prod->driver_info;
if (!info) {
dev_dbg (&udev->dev, "blacklisted by %s\n", name);
return -ENODEV;
}
xdev = interface_to_usbdev (udev); //通过接口获取设备
interface = udev->cur_altsetting; //当前轮流设置的接口
status = -ENOMEM;
// set up our own records
net = alloc_etherdev(sizeof(*dev)); //动态分配一个usb以太网设备
if (!net)
goto out;
/* netdev_printk() needs this so do it as early as possible */
SET_NETDEV_DEV(net, &udev->dev);
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);
skb_queue_head_init (&dev->rxq); //初始化一个接收的skb队列
skb_queue_head_init (&dev->txq); //初始化一个发送的skb队列
skb_queue_head_init (&dev->done);
skb_queue_head_init(&dev->rxq_pause);
//初始化一个tasklet(工作于中断低半部)
dev->bh.func = usbnet_bh; //回调函数
dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent);
//初始化一个定时器
init_usb_anchor(&dev->deferred); //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"); //usb网卡名称
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;
#if 0
// dma_supported() is deeply broken on almost all architectures
// possible with some EHCI controllers
if (dma_supported (&udev->dev, DMA_BIT_MASK(64)))
net->features |= NETIF_F_HIGHDMA;
#endif
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) { //这里将调用 usbnet_cdc_bind接口,实行CDC驱动绑定,见下面的分析
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.
//根据驱动标识,给不同的网络设备命名,上面默认是strcpy (net->name, "usb%d"); //usb网卡名称
//根据上面cdc_info结构体的成员,知道dev->driver_info->flags=FLAG_ETHER | FLAG_POINTTOPOINT
//所以,下面的条件都不成立,即为strcpy (net->name, "usb%d")
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");
/* 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);
else
status = 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);
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);
status = register_netdev (net); //最后注册网络设备
if (status)
goto out4;
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;
out4:
usb_free_urb(dev->interrupt);
out3:
if (info->unbind)
info->unbind (dev, udev);
out1:
free_netdev(net);
out:
return status;
}
EXPORT_SYMBOL_GPL(usbnet_probe);
该探测函数内部的主要功能是:
a. 初始化队列
skb_queue_head_init (&dev->rxq); //初始化一个接收的skb队列
skb_queue_head_init (&dev->txq); //初始化一个发送的skb队列
skb_queue_head_init (&dev->done);
skb_queue_head_init(&dev->rxq_pause);
b. 初始化一个tasklet
//初始化一个tasklet(工作于中断低半部)
dev->bh.func = usbnet_bh; //回调函数
dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent);
c. 初始化一个skb定时器
//初始化一个定时器
init_usb_anchor(&dev->deferred); //deferred: 推迟、延期
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
init_timer (&dev->delay);
d. 给usb网卡命名
strcpy (net->name, "usb%d"); //usb网卡名称
memcpy (net->dev_addr, node_id, sizeof node_id);
e. 绑定网络设备驱动程序需要实现的函数集、看门狗超时、设置和报告网络的函数集
//该数据结构中包含了一组函数指针,是网络设备驱动程序需要实现的函数集
net->netdev_ops = &usbnet_netdev_ops;
net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
//该数据结构中包含了一组函数指针,驱动程序可以实现这组函数,支持修改和报告网络设备设置的函数集合
net->ethtool_ops = &usbnet_ethtool_ops;
static const struct net_device_ops usbnet_netdev_ops = {
.ndo_open = usbnet_open,
.ndo_stop = usbnet_stop,
.ndo_start_xmit = usbnet_start_xmit,
.ndo_tx_timeout = usbnet_tx_timeout,
.ndo_change_mtu = usbnet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
下面具体分析usbnet_netdev_ops的各字段成员
e.1 usbnet_open
int usbnet_open (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
int retval;
struct driver_info *info = dev->driver_info; //这个对应上面的 cdc_info 结构体
if ((retval = usb_autopm_get_interface(dev->intf)) < 0) {
netif_info(dev, ifup, dev->net,
"resumption fail (%d) usbnet usb-%s-%s, %s\n",
retval,
dev->udev->bus->bus_name,
dev->udev->devpath,
info->description);
goto done_nopm;
}
// put into "known safe" state
if (info->reset && (retval = info->reset (dev)) < 0) { //cdc_info 结构体内部没有定义该字段
netif_info(dev, ifup, dev->net,
"open reset fail (%d) usbnet usb-%s-%s, %s\n",
retval,
dev->udev->bus->bus_name,
dev->udev->devpath,
info->description);
goto done;
}
// insist peer be connected
if (info->check_connect && (retval = info->check_connect (dev)) < 0) { //cdc_info 结构体内部没有定义该字段
netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval);
goto done;
}
/* start any status interrupt transfer */
if (dev->interrupt) {
retval = usbnet_status_start(dev, GFP_KERNEL);
if (retval < 0) {
netif_err(dev, ifup, dev->net,
"intr submit %d\n", retval);
goto done;
}
}
set_bit(EVENT_DEV_OPEN, &dev->flags);
netif_start_queue (net);
netif_info(dev, ifup, dev->net,
"open: enable queueing (rx %d, tx %d) mtu %d %s framing\n",
(int)RX_QLEN(dev), (int)TX_QLEN(dev),
dev->net->mtu,
(dev->driver_info->flags & FLAG_FRAMING_NC) ? "NetChip" :
(dev->driver_info->flags & FLAG_FRAMING_GL) ? "GeneSys" :
(dev->driver_info->flags & FLAG_FRAMING_Z) ? "Zaurus" :
(dev->driver_info->flags & FLAG_FRAMING_RN) ? "RNDIS" :
(dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" :
"simple");
/* reset rx error state */
dev->pkt_cnt = 0;
dev->pkt_err = 0;
clear_bit(EVENT_RX_KILL, &dev->flags);
// delay posting reads until we're fully open
tasklet_schedule (&dev->bh);
if (info->manage_power) { //cdc_info 结构体内部定义了该字段,详见usbnet_manage_power()
retval = info->manage_power(dev, 1);
if (retval < 0) {
retval = 0;
set_bit(EVENT_NO_RUNTIME_PM, &dev->flags);
} else {
usb_autopm_put_interface(dev->intf);
}
}
return retval;
done:
usb_autopm_put_interface(dev->intf);
done_nopm:
return retval;
}
usbnet_open函数内部主要时打开usb0网络设备
set_bit(EVENT_DEV_OPEN, &dev->flags);
然后启动任务
tasklet_schedule (&dev->bh); //启动任务
最后调用usbnet电源管理接口
if (info->manage_power) { //cdc_info 结构体内部定义了该字段,详见usbnet_manage_power()
retval = info->manage_power(dev, 1);
int usbnet_manage_power(struct usbnet *dev, int on)
{
dev->intf->needs_remote_wakeup = on;
return 0;
}
EXPORT_SYMBOL(usbnet_manage_power);
e.2 usbnet_stop
int usbnet_stop (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
struct driver_info *info = dev->driver_info;
int retval;
clear_bit(EVENT_DEV_OPEN, &dev->flags); //清除打开usb网络标识
netif_stop_queue (net);
netif_info(dev, ifdown, dev->net,
"stop stats: rx/tx %lu/%lu, errs %lu/%lu\n",
net->stats.rx_packets, net->stats.tx_packets,
net->stats.rx_errors, net->stats.tx_errors);
/* allow minidriver to stop correctly (wireless devices to turn off
* radio etc) */
if (info->stop) { //cdc_info 中 stop字段未定义
retval = info->stop(dev);
if (retval < 0)
netif_info(dev, ifdown, dev->net,
"stop fail (%d) usbnet usb-%s-%s, %s\n",
retval,
dev->udev->bus->bus_name, dev->udev->devpath,
info->description);
}
if (!(info->flags & FLAG_AVOID_UNLINK_URBS))
usbnet_terminate_urbs(dev);
usbnet_status_stop(dev);
usbnet_purge_paused_rxq(dev);
/* deferred work (task, timer, softirq) must also stop.
* can't flush_scheduled_work() until we drop rtnl (later),
* else workers could deadlock; so make workers a NOP.
*/
dev->flags = 0;
del_timer_sync (&dev->delay); //删掉定时器
tasklet_kill (&dev->bh); //删掉tasklet任务
if (info->manage_power &&
!test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags))
info->manage_power(dev, 0);
else
usb_autopm_put_interface(dev->intf);
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_stop);
e.3 usbnet_start_xmit()
usb网络启动发送,详见代码
e.4 usbnet_tx_timeout
usb网络发送超时处理,详见代码
e.5 usbnet_change_mtu
usbnet mtu最大传输单元改变,详见代码
e.6 eth_mac_addr /eth_validate_addr
网络MAC地址改变和MAC地址是否有效,详见代码
f. usbnet_ethtool_ops工具操作
static const struct ethtool_ops usbnet_ethtool_ops = {
.get_settings = usbnet_get_settings,
.set_settings = usbnet_set_settings,
.get_link = usbnet_get_link,
.nway_reset = usbnet_nway_reset,
.get_drvinfo = usbnet_get_drvinfo,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
.get_ts_info = ethtool_op_get_ts_info,
};
f.1 usbnet_get_settings
int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd)
{
struct usbnet *dev = netdev_priv(net);
if (!dev->mii.mdio_read) //mii的读取接口是否为NULL
return -EOPNOTSUPP;
return mii_ethtool_gset(&dev->mii, cmd); //详见源码
}
EXPORT_SYMBOL_GPL(usbnet_get_settings);
f.2 usbnet_set_settings
int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd)
{
struct usbnet *dev = netdev_priv(net);
int retval;
if (!dev->mii.mdio_write)
return -EOPNOTSUPP;
retval = mii_ethtool_sset(&dev->mii, cmd); //详见源码
/* link speed/duplex might have changed */
if (dev->driver_info->link_reset)
dev->driver_info->link_reset(dev);
return retval;
}
EXPORT_SYMBOL_GPL(usbnet_set_settings);
f.3 usbnet_get_link
获取网络链路状态,详见源码。
f.4 usbnet_nway_reset
网络自动协商复位,详见源码
f.5 usbnet_get_drvinfo
获取驱动信息
void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
{
struct usbnet *dev = netdev_priv(net);
strlcpy (info->driver, dev->driver_name, sizeof info->driver);
strlcpy (info->version, DRIVER_VERSION, sizeof info->version);
strlcpy (info->fw_version, dev->driver_info->description,
sizeof info->fw_version);
usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
}
EXPORT_SYMBOL_GPL(usbnet_get_drvinfo);
f.6 usbnet_get_msglevel
获取消息使能状态
u32 usbnet_get_msglevel (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
return dev->msg_enable;
}
EXPORT_SYMBOL_GPL(usbnet_get_msglevel);
f.7 usbnet_set_msglevel
设置消息使能状态
void usbnet_set_msglevel (struct net_device *net, u32 level)
{
struct usbnet *dev = netdev_priv(net);
dev->msg_enable = level;
}
EXPORT_SYMBOL_GPL(usbnet_set_msglevel);
f.8 ethtool_op_get_ts_info
获取时间戳信息
int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
{
info->so_timestamping =
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
info->phc_index = -1;
return 0;
}
EXPORT_SYMBOL(ethtool_op_get_ts_info);
g. cdc_info->bind绑定
if (info->bind) { //这里将调用 usbnet_cdc_bind接口,实行CDC驱动绑定,见下面的分析
status = info->bind (dev, udev);
先看下info指向的结构体
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT,
// .check_connect = cdc_check_connect,
.bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = usbnet_cdc_status,
.manage_power = usbnet_manage_power,
};
其中usbnet_cdc_bind源码如下:
int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status;
struct cdc_state *info = (void *) &dev->data; //
BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
< sizeof(struct cdc_state)));
status = usbnet_generic_cdc_bind(dev, intf);
if (status < 0)
return status;
status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress); //获取MAC地址
if (status < 0) {
usb_set_intfdata(info->data, NULL);
usb_driver_release_interface(driver_of(intf), info->data);
return status;
}
/* FIXME cdc-ether has some multicast code too, though it complains
* in routine cases. info->ether describes the multicast support.
* Implement that here, manipulating the cdc filter as needed.
*/
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
到这里usbnet_probe相应的函数初始化完了,接下来将该usbnet进行注册....
6. usbnet网络注册
status = register_netdev (net); //最后注册网络设备
int register_netdev(struct net_device *dev)
{
int err;
rtnl_lock();
err = register_netdevice(dev); //注册网络设备
rtnl_unlock();
return err;
}
EXPORT_SYMBOL(register_netdev);
int register_netdevice(struct net_device *dev)
{
int ret;
struct net *net = dev_net(dev);
BUG_ON(dev_boot_phase);
ASSERT_RTNL();
might_sleep();
/* When net_device's are persistent, this will be fatal. */
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
BUG_ON(!net);
spin_lock_init(&dev->addr_list_lock);
netdev_set_addr_lockdep_class(dev);
dev->iflink = -1;
ret = dev_get_valid_name(net, dev, dev->name); //分配一个网卡设备,即"usb0"
if (ret < 0)
goto out;
/* Init, if this function is available */
if (dev->netdev_ops->ndo_init) { //该函数为NULL
ret = dev->netdev_ops->ndo_init(dev);
if (ret) {
if (ret > 0)
ret = -EIO;
goto out;
}
}
if (((dev->hw_features | dev->features) &
NETIF_F_HW_VLAN_CTAG_FILTER) &&
(!dev->netdev_ops->ndo_vlan_rx_add_vid ||
!dev->netdev_ops->ndo_vlan_rx_kill_vid)) {
netdev_WARN(dev, "Buggy VLAN acceleration in driver!\n");
ret = -EINVAL;
goto err_uninit;
}
ret = -EBUSY;
if (!dev->ifindex)
dev->ifindex = dev_new_index(net);
else if (__dev_get_by_index(net, dev->ifindex))
goto err_uninit;
if (dev->iflink == -1)
dev->iflink = dev->ifindex;
/* Transfer changeable features to wanted_features and enable
* software offloads (GSO and GRO).
*/
dev->hw_features |= NETIF_F_SOFT_FEATURES;
dev->features |= NETIF_F_SOFT_FEATURES;
dev->wanted_features = dev->features & dev->hw_features;
/* Turn on no cache copy if HW is doing checksum */
if (!(dev->flags & IFF_LOOPBACK)) {
dev->hw_features |= NETIF_F_NOCACHE_COPY;
if (dev->features & NETIF_F_ALL_CSUM) {
dev->wanted_features |= NETIF_F_NOCACHE_COPY;
dev->features |= NETIF_F_NOCACHE_COPY;
}
}
/* Make NETIF_F_HIGHDMA inheritable to VLAN devices.
*/
dev->vlan_features |= NETIF_F_HIGHDMA;
/* Make NETIF_F_SG inheritable to tunnel devices.
*/
dev->hw_enc_features |= NETIF_F_SG;
ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
ret = notifier_to_errno(ret);
if (ret)
goto err_uninit;
ret = netdev_register_kobject(dev); //注册网络内核对象object
if (ret)
goto err_uninit;
dev->reg_state = NETREG_REGISTERED;
__netdev_update_features(dev);
/*
* Default initial state at registry is that the
* device is present.
*/
set_bit(__LINK_STATE_PRESENT, &dev->state);
linkwatch_init_dev(dev);
dev_init_scheduler(dev);
dev_hold(dev);
list_netdevice(dev);
add_device_randomness(dev->dev_addr, dev->addr_len);
/* If the device has permanent device address, driver should
* set dev_addr and also addr_assign_type should be set to
* NET_ADDR_PERM (default value).
*/
if (dev->addr_assign_type == NET_ADDR_PERM)
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
/* Notify protocols, that a new device appeared. */
ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
ret = notifier_to_errno(ret);
if (ret) {
rollback_registered(dev);
dev->reg_state = NETREG_UNREGISTERED;
}
/*
* Prevent userspace races by waiting until the network
* device is fully setup before sending notifications.
*/
if (!dev->rtnl_link_ops ||
dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U);
out:
return ret;
err_uninit:
if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev);
goto out;
}
EXPORT_SYMBOL(register_netdevice);
7. 应用层打开usb0网络设备时的堆栈信息
<c0012df8>] (dump_backtrace+0x0/0x10c) from [<c0012f1c>] (show_stack+0x18/0x1c)
r7:c035d374 r6:00001043 r5:c3b593a0 r4:c3b59000
[<c0012f04>] (show_stack+0x0/0x1c) from [<c03244ac>] (dump_stack+0x20/0x2c)
[<c032448c>] (dump_stack+0x0/0x2c) from [<c0238f60>] (usbnet_open+0x20/0x2a0)
[<c0238f40>] (usbnet_open+0x0/0x2a0) from [<c02b2e60>] (__dev_open+0xb8/0x114)
r8:00008914 r7:00001002 r6:00001043 r5:c035d3d0 r4:c3b59000
[<c02b2da8>] (__dev_open+0x0/0x114) from [<c02af9c0>] (__dev_change_flags+0x94/0x13c)
r5:c3b59000 r4:00000041
[<c02af92c>] (__dev_change_flags+0x0/0x13c) from [<c02b2d70>] (dev_change_flags+0x18/0x50)
r7:c047b468 r6:beafbc00 r5:00001002 r4:c3b59000
[<c02b2d58>] (dev_change_flags+0x0/0x50) from [<c0305fd0>] (devinet_ioctl+0x698/0x780)
r7:c047b468 r6:beafbc00 r5:00000001 r4:c2ee4000
[<c0305938>] (devinet_ioctl+0x0/0x780) from [<c0307414>] (inet_ioctl+0x1b4/0x1c4)
[<c0307260>] (inet_ioctl+0x0/0x1c4) from [<c029dd2c>] (sock_ioctl+0x70/0x29c)
[<c029dcbc>] (sock_ioctl+0x0/0x29c) from [<c00d62d8>] (do_vfs_ioctl+0x88/0x638)
r7:c3549700 r6:00000003 r5:c3adc460 r4:00000003
[<c00d6250>] (do_vfs_ioctl+0x0/0x638) from [<c00d68cc>] (SyS_ioctl+0x44/0x68)
[<c00d6888>] (SyS_ioctl+0x0/0x68) from [<c000f7c0>] (ret_fast_syscall+0x0/0x2c)
r8:c000f948 r7:00000036 r6:beafbddc r5:beafbc00 r4:000ac169
usb 1-1: link qh32-0001/c2ecf080 start 2 [1/0 us]
具体分析该函数内部定义的成员,并且分析bind的机制...