cyliu
论坛版主
论坛版主
  • 注册日期2003-06-13
  • 最后登录2014-04-11
  • 粉丝5
  • 关注0
  • 积分1238分
  • 威望2531点
  • 贡献值0点
  • 好评度577点
  • 原创分14分
  • 专家分10分
阅读:4677回复:2

USB – NET笔记

楼主#
更多 发布于:2007-06-04 16:29
author : cyliu

date : 2007-03-20

1.    USB-NET说明
本分析是建立在linux2.6.20内核基础上。
2.    USB-NET系统结构图
 
                              
a linux实现
    linux主要实现两个接口 – 发送接口(start_xmit) 和接收接口(netif_rx)
b windows实现
    Windows中主要对上实现minport,对下实现usb接口。可以修改ndis相关驱动即可实现。
3.    USB-NET源码分析
3.1.初始化部分
初始化部分主要是建立设备环境:创建/注册设备,配置设备参数等。
3.1.1 模块初始化
static int __init usbnet_init(void)
{
    /* 省略非重要代码若干行 */
    /* 生成一个网络mac地址 。node_id是全局变量,保存生成后的网络mac地址*/
    random_ether_addr(node_id);
     return 0;
}
下面让看看如何生成Mac地址的:
static inline void random_ether_addr(u8 *addr)
{
    /* 利用内核随机数生成器生成一个随机数:注意内核随机数生成器生成的随机数不能应用与安全算法方面 */
get_random_bytes (addr, ETH_ALEN);
    addr [0] &= 0xfe;    /* 去掉广播地址 */
    addr [0] |= 0x02;    /*设置本地地址(IEEE802) */
}
3.1.2 问题来了
不对吧,怎么没有创建/注册网络设备阿?当时看到这里的时候,我也有点奇怪。但知道肯定有地方来注册设备,哪里呢?也许设备的probe接口是个好地方。Go, 果真是这里。
int
usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{
    struct usbnet        *dev; /* 私有数据结构,作为net设备与usb设备之间的关联表*/
    struct net_device         *net; /* 网络设备 */
    struct usb_host_interface    *interface; /* usb interface */
    struct driver_info        *info;
    struct usb_device        *xdev; /* usb设备*/
    int                status;
    /* usb设备和net网络设备是比较重要 */
    
    info = (struct driver_info *) prod->driver_info;
    if (!info) {
        dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name);
        return -ENODEV;
    }
    xdev = interface_to_usbdev (udev);
    interface = udev->cur_altsetting;

    usb_get_dev (xdev);

    status = -ENOMEM;

    /* 创建网络设备,网络设备的私有数据区保存的是usbnet信息。为什么要这么做?目的是*在发送数据时能够找到相关的usb设备。
  struct net_device *alloc_etherdev(int sizeof_priv)
 {
        return alloc_netdev(sizeof_priv, "eth%d", ether_setup);
}
void ether_setup(struct net_device *dev)
{
    dev->change_mtu        = eth_change_mtu;
    dev->hard_header    = eth_header;
    dev->rebuild_header     = eth_rebuild_header;
    dev->set_mac_address     = eth_mac_addr;
    dev->hard_header_cache    = eth_header_cache;
    dev->header_cache_update= eth_header_cache_update;
    dev->hard_header_parse    = eth_header_parse;

    dev->type        = ARPHRD_ETHER;
    dev->hard_header_len     = ETH_HLEN;
    dev->mtu        = ETH_DATA_LEN;
    dev->addr_len        = ETH_ALEN;
    dev->tx_queue_len    = 1000;    /* Ethernet wants good queues */    
    dev->flags        = IFF_BROADCAST|IFF_MULTICAST;
    
    memset(dev->broadcast, 0xFF, ETH_ALEN);

}
* 以上是引用的两个参考接口
*/
    net = alloc_etherdev(sizeof(*dev));
    if (!net) {
        dbg ("can't kmalloc dev");
        goto out;
    }
    /* 设置网络设备参数 */
    dev = netdev_priv(net);
    dev->udev = xdev; /* 这里就是建立了net-usb的关联 */
    dev->driver_info = info;
    dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
                | NETIF_MSG_PROBE | NETIF_MSG_LINK);
    skb_queue_head_init (&dev->rxq);
    skb_queue_head_init (&dev->txq);
    skb_queue_head_init (&dev->done);
    dev->bh.func = usbnet_bh; /* 完成例程 */
    dev->bh.data = (unsigned long) dev;
    INIT_WORK (&dev->kevent, kevent);
    dev->delay.function = usbnet_bh; /* 完成例程 */
    dev->delay.data = (unsigned long) dev;
    init_timer (&dev->delay);
    mutex_init (&dev->phy_mutex);

    SET_MODULE_OWNER (net);
    dev->net = net; /* usbnet中保存了网络设备 */
    strcpy (net->name, "usb%d"); /* 网络设备名称从ethxxx改变为usbxxx*/
    memcpy (net->dev_addr, node_id, sizeof node_id); /* 设置网络mac地址 */

    /* 接收和发送数据消息大小可以是不同的。
     * bind() 时应该设置 rx_urb_size
     */
    dev->hard_mtu = net->mtu + net->hard_header_len;
    /* 配置网络设备接口 */
    net->change_mtu = usbnet_change_mtu;
    net->get_stats = usbnet_get_stats;
    net->hard_start_xmit = usbnet_start_xmit; /* 发送接口 */
    net->open = usbnet_open;
    net->stop = usbnet_stop;
    net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
    net->tx_timeout = usbnet_tx_timeout;
    net->ethtool_ops = &usbnet_ethtool_ops; /* ethtool使用的接口 */

    /* 设置usbnet,配置net和usb设备的联系接口 */
    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
                && (net->dev_addr [0] & 0x02) == 0)
            strcpy (net->name, "eth%d");  /* 网络设备名称从usbxxx改变为ethxxx*/

        /* 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 {
        /* 设置usbnet接口 */
        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);

    SET_NETDEV_DEV(net, &udev->dev);
    /* 注册网络设备 */
    status = register_netdev (net);
    if (status)
        goto out3;
    if (netif_msg_probe (dev))
        devinfo (dev, "register '%s' at usb-%s-%s, %s, "
                "%02x:%02x:%02x:%02x:%02x:%02x",
            udev->dev.driver->name,
            xdev->bus->bus_name, xdev->devpath,
            dev->driver_info->description,
            net->dev_addr [0], net->dev_addr [1],
            net->dev_addr [2], net->dev_addr [3],
            net->dev_addr [4], net->dev_addr [5]);

    /* 建立usb->usbnet的联系
     * 这里可以看出,net->usbnet<-usb,usbnet就是usb设备和net设备之间的纽带
     */
    usb_set_intfdata (udev, dev);

    /* 启动网络设备 */
    netif_device_attach (net);

    return 0;

out3:
    if (info->unbind)
        info->unbind (dev, udev);
out1:
    free_netdev(net);
out:
    usb_put_dev(xdev);
    return status;
}
由上可以知道,usb设备创建后,会调用probe接口。probe接口创建net设备,以提供给用户可见和使用。
3.1.3 问题又来了
现在知道了何地何时创建网络设备:就是在usb设备probe接口是创建。probe何时调用?蚂蚁都知道。
那么现在问题是usb设备什么时候创建阿,模块初始化里面并没有usb设备操作阿?很奇怪了。
本人还不算太笨。根据linux得整个体系结构,大多是简单明了,复用架构实现的得心应手。那么肯定是复用了一下usb设备。搜一下usbnet_probe果真如此,大约有asix模块,cdc_ether模块,cdc_subset模块,gl620a模块等等。下面我们以相对简单的plusb模块为例来说明。
/* UBS驱动 */
static struct usb_driver plusb_driver = {
    .name =        "plusb",
    .id_table =    products,
    .probe =    usbnet_probe, /* 注意这里哦 ,就是我们前面介绍的probe*/
    .disconnect =    usbnet_disconnect,
    .suspend =    usbnet_suspend,
    .resume =    usbnet_resume,
};
/* 模块初始化 */
static int __init plusb_init(void)
{
    /* 注册usb设备 */
     return usb_register(&plusb_driver);
}
/* 关于usb框架专门章节介绍,这里不再详细描述 */
3.1.4 总结
    一切都清楚了。plusb_init模块初始化时注册/创建usb设备。当发现有usb设备插入计算机后,会调用usb设备的probe,即usbnet_probe。在usbnet_probe中创建网络设备,并建立net设备与usb设备之间的关联。初始化就这样完成。
3.2.网络发送/接收过程
3.2.1 网络设备驱动发送过程
static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
{
    struct usbnet        *dev = netdev_priv(net); /* 重要的结构,Net-usb关系联表 */
    int            length;
    int            retval = NET_XMIT_SUCCESS;
    struct urb        *urb = NULL;
    struct skb_data        *entry;
    struct driver_info    *info = dev->driver_info;
    unsigned long        flags;

    // some devices want funky USB-level framing, for
    // win32 driver (usually) and/or hardware quirks
    if (info->tx_fixup) {
        skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
        if (!skb) {
            if (netif_msg_tx_err (dev))
                devdbg (dev, "can't tx_fixup skb");
            goto drop;
        }
    }
    length = skb->len;
    /* 1 关键点到了。申请urb,然后通过urb通信给usb设备 */
    if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
        if (netif_msg_tx_err (dev))
            devdbg (dev, "no urb");
        goto drop;
    }
    
    entry = (struct skb_data *) skb->cb;
    entry->urb = urb;
    entry->dev = dev;
    entry->state = tx_start;
    entry->length = length;
    /* 2 构造urb */
    usb_fill_bulk_urb (urb, dev->udev, dev->out,
            skb->data, skb->len, tx_complete, skb);

    /* don't assume the hardware handles USB_ZERO_PACKET
     * NOTE:  strictly conforming cdc-ether devices should expect
     * the ZLP here, but ignore the one-byte packet.
     *
     * FIXME zero that byte, if it doesn't require a new skb.
     */
    if ((length % dev->maxpacket) == 0)
        urb->transfer_buffer_length++;

    spin_lock_irqsave (&dev->txq.lock, flags);
    /* 3 写Usb设备 */
    switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
    /* 4 错误处理 */
    case -EPIPE:
        netif_stop_queue (net);
        usbnet_defer_kevent (dev, EVENT_TX_HALT);
        break;
    default:
        if (netif_msg_tx_err (dev))
            devdbg (dev, "tx: submit urb err %d", retval);
        break;
    case 0:
        net->trans_start = jiffies;
        __skb_queue_tail (&dev->txq, skb);
        if (dev->txq.qlen >= TX_QLEN (dev))
            netif_stop_queue (net);
    }
    spin_unlock_irqrestore (&dev->txq.lock, flags);

    if (retval) {
        if (netif_msg_tx_err (dev))
            devdbg (dev, "drop, code %d", retval);
drop:
        retval = NET_XMIT_SUCCESS;
        dev->stats.tx_dropped++;
        if (skb)
            dev_kfree_skb_any (skb);
        usb_free_urb (urb);
    } else if (netif_msg_tx_queued (dev)) {
        devdbg (dev, "> tx, len %d, type 0x%x",
            length, skb->protocol);
    }
    return retval;
}
static void tx_complete (struct urb *urb)
{
    struct sk_buff        *skb = (struct sk_buff *) urb->context;
    struct skb_data        *entry = (struct skb_data *) skb->cb;
    struct usbnet        *dev = entry->dev;

    /* 省略若干行 */

    urb->dev = NULL;
    entry->state = tx_done;
    defer_bh(dev, skb, &dev->txq); /* 把数据从发送队列送到done队列中 */
}
3.2.2 接收过程
接收过程要比发送过程处理复杂一些。我们知道,真实的网卡在有数据到来会主动触发网卡中断来处理到来的网络数据;那么对于usb设备就不同了,他没有数据到来事件来触发中断,因为它本身就是存储设备,不会主动向上层发送数据。那么Linux又是如何处理的呢?这里慢慢到来。
3.2.2.1 接收过程的原始触发点
    还记得probe里面关于网络设备的初始化过程吗?里面有一重要一个任务是初始化一个工作任务。来看看:
dev->bh.func = usbnet_bh; /* usbnet_bh也是十分重要的一点 */
dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent); /* 这句相当重要啊,是接收过程其起始点 */
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
来看看加粗部分kevent的接口处理:
static void
kevent (struct work_struct *work)
{
    struct usbnet        *dev =  container_of(work, struct usbnet, kevent);
    int            status;

    /* usb_clear_halt() needs a thread context */
    if (test_bit (EVENT_TX_HALT, &dev->flags)) {
        /* 省略若干行 */
    }
    if (test_bit (EVENT_RX_HALT, &dev->flags)) {
        /* 省略若干行 */
        } else {
            clear_bit (EVENT_RX_HALT, &dev->flags);
            tasklet_schedule (&dev->bh); /*引发软中断,调度usbnet_bh接口 */
        }
    }

    /* tasklet could resubmit itself forever if memory is tight */
    if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
        struct urb    *urb = NULL;
        /* 调用接收处理接口,并软中断 */
        if (netif_running (dev->net))
            urb = usb_alloc_urb (0, GFP_KERNEL);
        else
            clear_bit (EVENT_RX_MEMORY, &dev->flags);
        if (urb != NULL) {
            clear_bit (EVENT_RX_MEMORY, &dev->flags);
            rx_submit (dev, urb, GFP_KERNEL); /* 向usb设备发送数据请求 */
            tasklet_schedule (&dev->bh); /* 调度软中断,处理usb设备返回的数据
        }
    }

    if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
        /* 省略若干行 */
    }

    if (dev->flags)
        devdbg (dev, "kevent done, flags = 0x%lx",
            dev->flags);
}
小结:创建网络设备时初始化了一工作任务。内核会不断调度进程和工作任务。网络的工作任务完成数据向usb设备发送数据请求,并异步等待数据从usb设备返回。
3.2.2.2    rx_submit -向USB设备发送请求
static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
{
    struct sk_buff        *skb;
    struct skb_data        *entry;
    int            retval = 0;
    unsigned long        lockflags;
    size_t            size = dev->rx_urb_size;

    /* 分配skb数据空间 */
if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
        /* 省略若干行 */
        return;
    }
    skb_reserve (skb, NET_IP_ALIGN);

    entry = (struct skb_data *) skb->cb;
    entry->urb = urb;
    entry->dev = dev;
    entry->state = rx_start;
    entry->length = 0;
    
    usb_fill_bulk_urb (urb, dev->udev, dev->in,
        skb->data, size, rx_complete, skb); /*

    spin_lock_irqsave (&dev->rxq.lock, lockflags);

    if (netif_running (dev->net)
            && netif_device_present (dev->net)
            && !test_bit (EVENT_RX_HALT, &dev->flags)) {
        switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ /*向usb设备发送请求 */
        /* 错误处理,省略若干行 */
        default:
            if (netif_msg_rx_err (dev))
                devdbg (dev, "rx submit, %d", retval);
            tasklet_schedule (&dev->bh); /* 调度软中断 */
            break;
        case 0:
            __skb_queue_tail (&dev->rxq, skb); /* 把数据包从usb设备的接收队列转到网卡接收队列中,等待软中断调度 */
        }
    } else {
        /* 省略若干行 */
    }
    /* 省略若干行 */
    }
    rx_complete 是usb返回请求调用,从usb返回数据进入usb设备队列.
static void rx_complete (struct urb *urb)
{
    struct sk_buff        *skb = (struct sk_buff *) urb->context;
    struct skb_data        *entry = (struct skb_data *) skb->cb;
    struct usbnet        *dev = entry->dev;
    int            urb_status = urb->status;

    skb_put (skb, urb->actual_length);
    entry->state = rx_done;
    entry->urb = NULL;

    switch (urb_status) {
        /*省略若干行 */
    }

    defer_bh(dev, skb, &dev->rxq); /* 把数据从usb设备队列中到放到done队列中 */

    if (urb) {
        if (netif_running (dev->net)
                && !test_bit (EVENT_RX_HALT, &dev->flags)) {
            rx_submit (dev, urb, GFP_ATOMIC); /* 还有数据,继续处理 */
            return;
        }
        usb_free_urb (urb);
    }
    if (netif_msg_rx_err (dev))
        devdbg (dev, "no read resubmitted");
}

/* defer_bh是发送和接收公用的数据队列调度接口,发送数据和接收数据分别从usb设备的发送队列和接收队列转移到done队列中。那么是如何区分数据是应该发送还是接收呢? 原来在skb->cd中保留一个skb_data结构:
    struct skb_data {    /* skb->cb is one of these */
    struct urb        *urb; /* urb数据包 */
    struct usbnet        *dev; /* 转换表 */
    enum skb_state        state; /* 数据报状态:发送数据还是接收数据 */
    size_t            length;
};
enum skb_state {
    illegal = 0,
    tx_start, tx_done,
    rx_start, rx_done, rx_cleanup
};
    所以对于发送数据skb->cd->state = tx_done;对于接收数据skb->cd->state = rx_done。
static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
{
    unsigned long        flags;

    spin_lock_irqsave(&list->lock, flags);
    __skb_unlink(skb, list); /* 把数据从usb接收队列中摘出 */
    spin_unlock(&list->lock);
    spin_lock(&dev->done.lock);
    __skb_queue_tail(&dev->done, skb); /* 把数据放到done接收队列中 */
    if (dev->done.qlen == 1)
        tasklet_schedule(&dev->bh);
    spin_unlock_irqrestore(&dev->done.lock, flags);
}

3.2.2.3    usbnet_bh - 软中断处理函数
usbnet_bh是软中断处理函数(见前面网络设备初始化阶段),负责从发送和接收数据中断处理。这里我们只关心接收处理过程。
static void usbnet_bh (unsigned long param)
{
    struct usbnet        *dev = (struct usbnet *) param;
    struct sk_buff        *skb;
    struct skb_data        *entry;

    while ((skb = skb_dequeue (&dev->done))) { /* 数据报从队列中出队 */
        entry = (struct skb_data *) skb->cb;
        switch (entry->state) {
            case rx_done: /* 接收处理 */
            entry->state = rx_cleanup;
            rx_process (dev, skb); /* 接收处理流程 */
            continue;
            case tx_done:
            case rx_cleanup:
            /* 下面还所有重要处理,但是我们先不理睬了。省略若干行 */
    }
}
/* 调用网络栈接收数据接口 */
static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
{
    if (dev->driver_info->rx_fixup
            && !dev->driver_info->rx_fixup (dev, skb))
        goto error;
    // else network stack removes extra byte if we forced a short packet

    if (skb->len)
        usbnet_skb_return (dev, skb); /*数据开始进入网络栈前的准备阶段 */
    else {
        if (netif_msg_rx_err (dev))
            devdbg (dev, "drop");
error:
        dev->stats.rx_errors++;
        skb_queue_tail (&dev->done, skb);
    }
}
void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
{
    int    status;
    /* 终于到了数据报入网络栈的地方。*/
    /* 获取数据类型,并通过netif_rx接收数据 */
    /* 组织数据报,主要是网络入口设备和数据报协议类型 */
    skb->dev = dev->net;
    skb->protocol = eth_type_trans (skb, dev->net);
    dev->stats.rx_packets++;
    dev->stats.rx_bytes += skb->len;
    
    if (netif_msg_rx_status (dev))
        devdbg (dev, "< rx, len %zu, type 0x%x",
            skb->len + sizeof (struct ethhdr), skb->protocol);
    memset (skb->cb, 0, sizeof (struct skb_data));
    /* 噢噢,好熟悉的接口名字啊。对,数据报入网络栈了*/
status = netif_rx (skb);
    if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))
        devdbg (dev, "netif_rx status %d", status);
}
3.2.2.4 接收过程小结
    net 设备申请一工作任务&#61664;务在被调度时会向usb设备发送请求&#61664;usb设备返回请求的数据放到的net接收队列中&#61664;软中断负责把数据从net接收队列中送到Linux内核网络栈。
    从上可以看出,软中断以前是用对usb的操作模拟网卡的接收数据操作过程;软中断处理几乎和网卡的相同。
3.3.USB读/写过程
int usb_submit_urb(struct urb *urb, gfp_t mem_flags) /* 向usb设备发送请求 */
关于usb接口以后再介绍。
4.    USB-NET安全方面应用
走走看看开源好 Solaris vs Linux
zhaoyanghong
驱动小牛
驱动小牛
  • 注册日期2004-11-13
  • 最后登录2008-08-05
  • 粉丝0
  • 关注0
  • 积分341分
  • 威望92点
  • 贡献值0点
  • 好评度91点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2007-06-12 16:22
希望能列出use-net设备在USB驱动程序组中的位置以及与下层的接口及简介.
arthurcao
驱动小牛
驱动小牛
  • 注册日期2003-10-12
  • 最后登录2012-07-08
  • 粉丝0
  • 关注0
  • 积分92分
  • 威望20点
  • 贡献值0点
  • 好评度7点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2007-07-15 11:42
最好能给个整体的宏观描述,就更好了。
arthurcao喜欢开源。
游客

返回顶部