linux cdc_ether

1. 前言

RNDISRemote 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的机制...



`msm_cdc_set_supplies_lpm_mode` 函数是高通平台上用于设置CDC(Communications Device Class)供电低功耗模式的函数。该函数用于控制USB设备的电源管理,以实现低功耗操作。 以下是一个简单的示例: ```c #include <linux/usb/cdc.h> void msm_cdc_set_supplies_lpm_mode(struct usb_interface *intf, bool enable) { struct usb_cdc_ether_desc *desc; struct usb_cdc_ether_rndis *rndis; struct usb_cdc_notification *event; struct usb_device *dev; struct usb_driver *driver; struct usb_host_interface *iface_desc; int i; if (!intf) return; iface_desc = intf->cur_altsetting; if (!iface_desc) return; dev = interface_to_usbdev(intf); if (!dev) return; driver = dev->driver; if (!driver) return; desc = (struct usb_cdc_ether_desc *)iface_desc->desc; if (!desc) return; rndis = (struct usb_cdc_ether_rndis *)(desc + 1); if (!rndis) return; event = (struct usb_cdc_notification *)(rndis + 1); if (!event) return; for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { struct usb_host_endpoint *ep = &iface_desc->endpoint[i]; if (usb_endpoint_is_int_in(ep->desc)) break; } if (i >= iface_desc->desc.bNumEndpoints) { pr_err("msm_cdc_set_supplies_lpm_mode: No interrupt endpoint found\n"); return; } if (enable) { event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; event->bNotificationCode = USB_CDC_NOTIFY_NETWORK_CONNECTION_RES; } else { event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; event->bNotificationCode = USB_CDC_NOTIFY_NETWORK_CONNECTION_LOS; } usb_interrupt_msg(dev, usb_rcvintpipe(dev, ep->desc.bEndpointAddress), event, sizeof(*event), NULL, 0, 1000); } ``` 上述示例代码中,函数接受一个 `usb_interface` 结构体指针作为参数,以及一个布尔值 `enable`,用来控制是否启用低功耗模式。函数首先获得相关的USB设备和驱动程序,然后根据接口描述符和端点信息进行设置,最后通过USB中断传输发送通知。 需要注意的是,上述代码只是一个简化的示例,并不是完整的实现。实际使用时,还需要根据具体的平台和需求进行适当的调整和扩展。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值