解决“ipheth_tx_timeout: TX timeout”
解决“ipheth_tx_timeout: TX timeout”linux-3.3.8/drivers/net/usb/ipheth.c
·
解决“ipheth_tx_timeout: TX timeout”
在调试iOS的usb tethering时发现这个问题的。(如何搭建iOS的usb tethering请看我的这篇文章《OpenWrt支持usb tethering》)
其实当前主流Linux(如Ubuntu)使用的Diego Giagio作者的ipheth,功能性是好的,但稳定性不足。
废话少说
code:
<基于kernel 3.3.8>
linux-3.3.8/drivers/net/usb/ipheth.c
/*
* Copyright (C) 2017 Walker Wei<walker0411@163.com>. All rights reserved.
* Copyright (C) 2009 Diego Giagio. All rights reserved.
* Copyright (C) 2000-2005 by David Brownell
* Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Thanks to Diego Giagio for figuring out the programming details for
* the Apple iPhone Ethernet driver.
* This program is based on usbnet which is written by David Brownell.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/mii.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/usb/usbnet.h>
#define USB_VENDOR_APPLE 0x05ac
#define USB_PRODUCT_IPHONE 0x1290
#define USB_PRODUCT_IPHONE_3G 0x1292
#define USB_PRODUCT_IPHONE_3GS 0x1294
#define USB_PRODUCT_IPHONE_4 0x1297
#define USB_PRODUCT_IPHONE_4_VZW 0x129c
#define USB_PRODUCT_IPHONE_4S 0x12a0
#define USB_PRODUCT_IPHONE_5 0x12a8
#define USB_PRODUCT_IPAD 0x129a
#define USB_PRODUCT_IPAD_MINI 0x12ab
#define IPHETH_USBINTF_CLASS 255
#define IPHETH_USBINTF_SUBCLASS 253
#define IPHETH_USBINTF_PROTO 1
#define IPHETH_INTFNUM 2
#define IPHETH_ALT_INTFNUM 1
#define IPHETH_BUF_SIZE 1516
#define IPHETH_IP_ALIGN 2 /* padding at front of URB */
#define IPHETH_CTRL_ENDP 0x00
#define IPHETH_CTRL_BUF_SIZE 0x40
#define IPHETH_CTRL_TIMEOUT (5 * HZ)
#define IPHETH_CMD_GET_MACADDR 0x00
#define IPHETH_CMD_CARRIER_CHECK 0x45
#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ)
#define IPHETH_CARRIER_ON 0x04
static struct ipheth_local local;
struct ipheth_local {
struct usbnet *dev;
struct mutex local_mutex;
struct delayed_work carrier_work;
};
/* same as usbnet_netdev_ops but MTU change not allowed */
static const struct net_device_ops ipheth_netdev_ops = {
.ndo_open = usbnet_open,
.ndo_stop = usbnet_stop,
.ndo_start_xmit = usbnet_start_xmit,
.ndo_tx_timeout = usbnet_tx_timeout,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
void ipheth_status(struct usbnet *dev, struct urb *urb)
{
netdev_dbg(dev->net, "ipheth status urb, len %d stat %d\n",
urb->actual_length, urb->status);
}
EXPORT_SYMBOL_GPL(ipheth_status);
static int ipheth_carrier_set(struct usbnet *dev)
{
struct usb_device *udev = NULL;
int retval = 0;
unsigned char ctrl_buf[IPHETH_CTRL_BUF_SIZE] = {0};
if (NULL == dev)
{
err("%s: struct usbnet *dev is NULL.", __func__);
retval = -EINVAL;
return retval;
}
udev = dev->udev;
retval = usb_control_msg(udev,
usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP),
IPHETH_CMD_CARRIER_CHECK, /* request */
0xc0, /* request type */
0x00, /* value */
0x02, /* index */
(void *)ctrl_buf,
IPHETH_CTRL_BUF_SIZE,
IPHETH_CTRL_TIMEOUT);
if (retval < 0) {
err("%s: usb_control_msg: %d", __func__, retval);
}
else
{
if (ctrl_buf[0] == IPHETH_CARRIER_ON)
netif_carrier_on(dev->net);
else
netif_carrier_off(dev->net);
}
return retval;
}
static void ipheth_carrier_check_work(struct work_struct *work)
{
struct usbnet *dev = NULL;
mutex_lock(&(local.local_mutex));
dev = local.dev;
ipheth_carrier_set(dev);
schedule_delayed_work(&(local.carrier_work), IPHETH_CARRIER_CHECK_TIMEOUT);
mutex_unlock(&(local.local_mutex));
}
static int ipheth_get_macaddr(struct usbnet *dev)
{
struct usb_device *udev = dev->udev;
struct net_device *net = dev->net;
int retval;
unsigned char ctrl_buf[IPHETH_CTRL_BUF_SIZE] = {0};
retval = usb_control_msg(udev,
usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP),
IPHETH_CMD_GET_MACADDR, /* request */
0xc0, /* request type */
0x00, /* value */
0x02, /* index */
(void *)ctrl_buf,
IPHETH_CTRL_BUF_SIZE,
IPHETH_CTRL_TIMEOUT);
if (retval < 0) {
err("%s: usb_control_msg: %d", __func__, retval);
} else if (retval < ETH_ALEN) {
err("%s: usb_control_msg: short packet: %d bytes",
__func__, retval);
retval = -EINVAL;
} else {
memcpy(net->dev_addr, (const void *)ctrl_buf, ETH_ALEN);
memcpy(net->perm_addr, (const void *)ctrl_buf, ETH_ALEN);
retval = 0;
}
return retval;
}
static int ipheth_bind(struct usbnet *dev, struct usb_interface *intf)
{
int retval = 0;//success
int i = 0;
struct usb_host_interface *hintf;
struct usb_endpoint_descriptor *endp;
u8 bulk_in = 0;
u8 bulk_out = 0;
/* Set up endpoints */
hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
if (hintf == NULL) {
retval = -ENODEV;
err("Unable to find alternate settings interface, retval=%d", retval);
return retval;
}
for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
endp = &hintf->endpoint[i].desc;
if (usb_endpoint_is_bulk_in(endp))
bulk_in = endp->bEndpointAddress;
else if (usb_endpoint_is_bulk_out(endp))
bulk_out = endp->bEndpointAddress;
}
if (!(bulk_in && bulk_out)) {
retval = -ENODEV;
err("Unable to find endpoints, retval=%d", retval);
return retval;
}
else
{
dev->in = usb_rcvbulkpipe(dev->udev, bulk_in & USB_ENDPOINT_NUMBER_MASK);
dev->out= usb_sndbulkpipe(dev->udev, bulk_out & USB_ENDPOINT_NUMBER_MASK);
}
//fix bulk recv buffer size
dev->rx_urb_size = IPHETH_BUF_SIZE;
//dev->hard_mtu = IPHETH_BUF_SIZE;//need this?
//get hardware addr
retval = ipheth_get_macaddr(dev);
if (0 != retval)
{
err("Unable to get macaddr, retval=%d", retval);
return retval;
}
//carrier check:check the remote tethering enable or not.
mutex_init (&(local.local_mutex));
INIT_DELAYED_WORK(&(local.carrier_work), ipheth_carrier_check_work);
local.dev = dev;
return retval;
}
EXPORT_SYMBOL_GPL(ipheth_bind);
static int ipheth_reset(struct usbnet *dev)
{
int retval = 0;//success
struct work_struct *work = NULL;
//Attention: this func's section should not be in "reset" function,
//I put it here because it still need to be called during the usbnet_open().
usb_set_interface(dev->udev, IPHETH_INTFNUM, IPHETH_ALT_INTFNUM);
ipheth_carrier_check_work(work);
return retval;
}
EXPORT_SYMBOL_GPL(ipheth_reset);
static int ipheth_stop(struct usbnet *dev)
{
mutex_lock(&(local.local_mutex));
cancel_delayed_work_sync(&local.carrier_work);
mutex_unlock(&(local.local_mutex));
return 0;
}
EXPORT_SYMBOL_GPL(ipheth_stop);
int ipheth_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
skb_pull(skb, IPHETH_IP_ALIGN);
return 1;
}
EXPORT_SYMBOL_GPL(ipheth_rx_fixup);
static const struct driver_info ipheth_info = {
.description = "ipheth device",
.flags = FLAG_ETHER | FLAG_NO_SETINT,
.status = ipheth_status,
.bind = ipheth_bind,
.reset = ipheth_reset,
.stop = ipheth_stop,
.rx_fixup = ipheth_rx_fixup,
};
/*-------------------------------------------------------------------------*/
static struct usb_device_id ipheth_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3G,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3GS,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4S,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPAD,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPAD_MINI,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ USB_DEVICE_AND_INTERFACE_INFO(
USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_5,
IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
IPHETH_USBINTF_PROTO),
.driver_info = (unsigned long) &ipheth_info,},
{ }
};
MODULE_DEVICE_TABLE(usb, ipheth_table);
static struct usb_driver ipheth_driver = {
.name = "ipheth",
.id_table = ipheth_table,
.probe = usbnet_probe,
.disconnect = usbnet_disconnect,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
};
module_usb_driver(ipheth_driver);
MODULE_AUTHOR("Walker Wei");
MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
MODULE_LICENSE("GPL");
思路
android使用的usb tethering是基于Rndis的,一个Microsoft提出的usb tethering协议,它的结构是Rndis协议层+usb net,而usb net正是一个封装了USB networking的架构,将usb设备虚拟成Ethernet adapters。
usb net应该比较稳定了,因为不但Rndis使用,其他的一些usb net adapter产品,如亚信的AX88179、AX88772B等也都是基于usb net来做。
因此,本次的修改就是将原有的ipheth移植到usb net上,变成ipheth+usb net的形势,经测试,稳定多了,尤其是大量数据传输的时候。
当前的code只是测试版本,还有很多优化的空间,后续再更新。
更多推荐
已为社区贡献1条内容
所有评论(0)