三:传输过程的实现
说到传输过程,我们必须要从URB开始说起,这个结构的就好比是网络子系统中的skb,好比是I/O中的bio.USB系统的信息传输就是打成URB结构,然后再过行传送的.
URB的全称叫USB request block.下面从它的接口说起.
3.1:URB的相关接口
1:URB的创建
URB的创建是由usb_alloc_urb()完成的.这个函数会完成URB内存的分配和基本成员的初始化工作.代码如下:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
   struct urb *urb;

urb = kmalloc(sizeof(struct urb) +
        iso_packets * sizeof(struct usb_iso_packet_descriptor),
        mem_flags);
    if (!urb) {
        err("alloc_urb: kmalloc failed");
        return NULL;
    }
    usb_init_urb(urb);
    return urb;
}
这个函数有两个参数,一个是iso_packets.仅仅用于ISO传输.表示ISO数据包个数,如果用于其它类型的传输,此参数为0.另一个是mem_flags.是分配内存的参数.
Usb_init_urb()如下:
void usb_init_urb(struct urb *urb)
{
    if (urb) {
        memset(urb, 0, sizeof(*urb));
        kref_init(&urb->kref);
        INIT_LIST_HEAD(&urb->anchor_list);
    }
}
由此可以看到,它的初始化只是初始化了引用计数和ahchor_list链表.这个链表在URB被锁定的时候会用到.

2:URB的初始化
USB2.0 spec中定义了四种传输,为别为ISO,INTER,BULK,CONTORL.linux kernel为INTER,BULK,CONTORL的URB初始化提供了一些API,ISO的传输只能够手动去初始化.这些API如下:
static inline void usb_fill_control_urb(struct urb *urb,
                    struct usb_device *dev,
                    unsigned int pipe,
                    unsigned char *setup_packet,
                    void *transfer_buffer,
                    int buffer_length,
                    usb_complete_t complete_fn,
                    void *context)
static inline void usb_fill_bulk_urb(struct urb *urb,
                     struct usb_device *dev,
                     unsigned int pipe,
                     void *transfer_buffer,
                     int buffer_length,
                     usb_complete_t complete_fn,
                     void *context)
static inline void usb_fill_int_urb(struct urb *urb,
                    struct usb_device *dev,
                    unsigned int pipe,
                    void *transfer_buffer,
                    int buffer_length,
                    usb_complete_t complete_fn,
                    void *context,
                    int interval)
分别用来填充CONTORL,BULK,INT类型的URB.
观察他们的函数原型,发现有很多相的的参数.先对这些参数做一下解释:
Urb:是要初始化的urb
Dev:表示消息要被发送到的USB设备
Pipe:表示消息被发送到的端点
transfer_buffer:表示发送数据的缓冲区
length:就是transfer_buffer所表示的缓冲区大小
context:完成处理函数的上下文
complete_fn:传输完了之后要调用的函数.
usb_fill_control_urb()的setup_packet:即将被发送到端点的设备数据包
usb_fill_int_urb()中的interval:这个urb应该被调度的间隔.
函数的实际都是差不多的.以usb_fill_control_urb()为例:
static inline void usb_fill_control_urb(struct urb *urb,
                    struct usb_device *dev,
                    unsigned int pipe,
                    unsigned char *setup_packet,
                    void *transfer_buffer,
                    int buffer_length,
                    usb_complete_t complete_fn,
                    void *context)
{
    urb->dev = dev;
    urb->pipe = pipe;
    urb->setup_packet = setup_packet;
    urb->transfer_buffer = transfer_buffer;
    urb->transfer_buffer_length = buffer_length;
    urb->complete = complete_fn;
    urb->context = context;
}
如上所示,只是将函数的参数赋值给了URB相关的成员而已.
另外,关于ISO的URB初始化虽然没有可以调用的API,但它的初始化也很简单,对应就是填充几个成员而已.
另外,对于pipe的参数.有一系列辅助的宏.如下示:
/* Create various pipes... */
#define usb_sndctrlpipe(dev,endpoint)   \
    ((PIPE_CONTROL 
#define usb_rcvctrlpipe(dev,endpoint)   \
    ((PIPE_CONTROL 
#define usb_sndisocpipe(dev,endpoint)   \
    ((PIPE_ISOCHRONOUS 
#define usb_rcvisocpipe(dev,endpoint)   \
    ((PIPE_ISOCHRONOUS 
#define usb_sndbulkpipe(dev,endpoint)   \
    ((PIPE_BULK 
#define usb_rcvbulkpipe(dev,endpoint)   \
    ((PIPE_BULK 
#define usb_sndintpipe(dev,endpoint)    \
    ((PIPE_INTERRUPT 
#define usb_rcvintpipe(dev,endpoint)    \
    ((PIPE_INTERRUPT 
这个宏都是根据usb2.0 spec的规范来设计的.

3:提交URB
提交urb的接口是usb_submit_urb().代码如下:
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
    int             xfertype, max;
    struct usb_device       *dev;
    struct usb_host_endpoint    *ep;
    int             is_out;

if (!urb || urb->hcpriv || !urb->complete)
        return -EINVAL;
    dev = urb->dev;
    if ((!dev) || (dev->state 
        return -ENODEV;

/* For now, get the endpoint from the pipe.  Eventually drivers
     * will be required to set urb->ep directly and we will eliminate
     * urb->pipe.
     */

//取得要传输的端口.对端地址是由方向+dev address+port number组成的
    ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
            [usb_pipeendpoint(urb->pipe)];
    if (!ep)
        return -ENOENT;

urb->ep = ep;
    urb->status = -EINPROGRESS;
    urb->actual_length = 0;

/* Lots of sanity checks, so HCDs can rely on clean data
     * and don't need to duplicate tests
     */
     //取得ep的传输类型
    xfertype = usb_endpoint_type(&ep->desc);
    //如果是控制传输.端点0默认是控制传输
    if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
        //控制传输的urb如果没有setup_packet是非法的
        struct usb_ctrlrequest *setup =
                (struct usb_ctrlrequest *) urb->setup_packet;

if (!setup)
            return -ENOEXEC;
        //判断是否是out方向的传输
        is_out = !(setup->bRequestType & USB_DIR_IN) ||
                !setup->wLength;
    } else {
        //如果不是控制传输,在端点描述符的bEndportAddress的bit7 包含有端点的传输方向
        is_out = usb_endpoint_dir_out(&ep->desc);
    }

/* Cache the direction for later use */
    //根据传输方向.置urb->transfer_flags的方向位
    urb->transfer_flags = (urb->transfer_flags & ~URB_DIR_MASK) |
            (is_out ? URB_DIR_OUT : URB_DIR_IN);

//根据usb2.0 spec.除控制传输外的其它传输只有在config状态的时候才能进行
    if (xfertype != USB_ENDPOINT_XFER_CONTROL &&
            dev->state 
        return -ENODEV;

//传送/接收的最大字节.如果这个最大巧若拙字节还要小于0,那就是非法的
    max = le16_to_cpu(ep->desc.wMaxPacketSize);
    if (max 
        dev_dbg(&dev->dev,
            "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
            usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
            __FUNCTION__, max);
        return -EMSGSIZE;
    }

/* periodic transfers limit size per frame/uframe,
     * but drivers only control those sizes for ISO.
     * while we're checking, initialize return status.
     */
     //如果是实时传输
    if (xfertype == USB_ENDPOINT_XFER_ISOC) {
        int n, len;

/* "high bandwidth" mode, 1-3 packets/uframe? */
        //如果是高速传输.则要修正它的MAX值
        //高速传输时, 一个微帧内可以修输多个数据.bit 11~bit12用来表示一个微帧内
        //传输包的个数.
        //在USB1.1中是不支持HIGH的
        if (dev->speed == USB_SPEED_HIGH) {
            int mult = 1 + ((max >> 11) & 0x03);
            max &= 0x07ff;
            max *= mult;
        }

//实现传输的数据包数目不能小于等于0
        if (urb->number_of_packets 
            return -EINVAL;
        //urb->number_of_packets: 实时数据包个数.每个实时数据包对应urb->iso_frame_desc[]中的一项
        for (n = 0; n number_of_packets; n++) {
            len = urb->iso_frame_desc[n].length;
            if (len  max)
                return -EMSGSIZE;
            urb->iso_frame_desc[n].status = -EXDEV;
            urb->iso_frame_desc[n].actual_length = 0;
        }
    }

/* the I/O buffer must be mapped/unmapped, except when length=0 */
    //如果要传输的缓存区大小小于0.非法
    if (urb->transfer_buffer_length 
        return -EMSGSIZE;

#ifdef DEBUG
    /* stuff that drivers shouldn't do, but which shouldn't
     * cause problems in HCDs if they get it wrong.
     */
    {
    unsigned int    orig_flags = urb->transfer_flags;
    unsigned int    allowed;

/* enforce simple/standard policy */
    allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
            URB_NO_INTERRUPT | URB_DIR_MASK | URB_FREE_BUFFER);
    switch (xfertype) {
    case USB_ENDPOINT_XFER_BULK:
        if (is_out)
            allowed |= URB_ZERO_PACKET;
        /* FALLTHROUGH */
    case USB_ENDPOINT_XFER_CONTROL:
        allowed |= URB_NO_FSBR; /* only affects UHCI */
        /* FALLTHROUGH */
    default:            /* all non-iso endpoints */
        if (!is_out)
            allowed |= URB_SHORT_NOT_OK;
        break;
    case USB_ENDPOINT_XFER_ISOC:
        allowed |= URB_ISO_ASAP;
        break;
    }
    urb->transfer_flags &= allowed;

/* fail if submitter gave bogus flags */
    if (urb->transfer_flags != orig_flags) {
        err("BOGUS urb flags, %x --> %x",
            orig_flags, urb->transfer_flags);
        return -EINVAL;
    }
    }
#endif
    /*
     * Force periodic transfer intervals to be legal values that are
     * a power of two (so HCDs don't need to).
     *
     * FIXME want bus->{intr,iso}_sched_horizon values here.  Each HC
     * supports different values... this uses EHCI/UHCI defaults (and
     * EHCI can use smaller non-default values).
     */

//关于实时传输和中断传输的interval处理
    switch (xfertype) {
    case USB_ENDPOINT_XFER_ISOC:
    case USB_ENDPOINT_XFER_INT:
        /* too small? */
        //interval不能小于或等于0
        if (urb->interval 
            return -EINVAL;
        /* too big? */
        switch (dev->speed) {
        case USB_SPEED_HIGH:    /* units are microframes */
            /* NOTE usb handles 2^15 */
            if (urb->interval > (1024 * 8))
                urb->interval = 1024 * 8;
            max = 1024 * 8;
            break;
        case USB_SPEED_FULL:    /* units are frames/msec */
        case USB_SPEED_LOW:
            if (xfertype == USB_ENDPOINT_XFER_INT) {
                if (urb->interval > 255)
                    return -EINVAL;
                /* NOTE ohci only handles up to 32 */
                max = 128;
            } else {
                if (urb->interval > 1024)
                    urb->interval = 1024;
                /* NOTE usb and ohci handle up to 2^15 */
                max = 1024;
            }
            break;
        default:
            return -EINVAL;
        }
        /* Round down to a power of 2, no more than max */
        urb->interval = min(max, 1 interval));
    }

return usb_hcd_submit_urb(urb, mem_flags);
}
这段代码虽然很长,但逻辑很清楚.对照代码中的注释理解应该是没有问题的.在这里要注意,UHCI是属于USB1.1的,它不支持HIGH传输.
对URB进行一系列处理之后,就会将urb丢给hcd进行处理了.usb_hcd_submit_urb()代码如下:
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
    int         status;
    //从usb_bus的地址取得usb_hcd的地址
    struct usb_hcd      *hcd = bus_to_hcd(urb->dev->bus);

/* increment urb's reference count as part of giving it to the HCD
     * (which will control it).  HCD guarantees that it either returns
     * an error or calls giveback(), but not both.
     */
     //增加有关的引用计数,usbmon*系列的函数是编译选择的.忽略
    usb_get_urb(urb);
    atomic_inc(&urb->use_count);
    atomic_inc(&urb->dev->urbnum);
    usbmon_urb_submit(&hcd->self, urb);

/* NOTE requirements on root-hub callers (usbfs and the hub
     * driver, for now):  URBs' urb->transfer_buffer must be
     * valid and usb_buffer_{sync,unmap}() not be needed, since
     * they could clobber root hub response data.  Also, control
     * URBs must be submitted in process context with interrupts
     * enabled.
     */
     //对传输的缓存区进行DMA映射
    status = map_urb_for_dma(hcd, urb, mem_flags);
    //出现错误,返回
    if (unlikely(status)) {
        usbmon_urb_submit_error(&hcd->self, urb, status);
        goto error;
    }

//如果是root hub
    if (is_root_hub(urb->dev))
        status = rh_urb_enqueue(hcd, urb);
    else
        //如果是一般的设备 
        status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);

if (unlikely(status)) {
        usbmon_urb_submit_error(&hcd->self, urb, status);
        unmap_urb_for_dma(hcd, urb);
error:
        urb->hcpriv = NULL;
        INIT_LIST_HEAD(&urb->urb_list);
        atomic_dec(&urb->use_count);
        atomic_dec(&urb->dev->urbnum);
        if (urb->reject)
            wake_up(&usb_kill_urb_queue);
        usb_put_urb(urb);
    }
    return status;
}
在这里函数里要注意到,urb->transfer_buffer是一个虚拟地址,用于UHCI的时候,必须要将其映射物理地址,以供设备使用.这 也就是map_urb_for_dma()要完成的工作. map_urb_for_dma()函数比较简单,这里就不做详细分析.
可能有人会有这样的疑惑,对于root hub的情况,为什么不用对传输缓存区进行DMA映射呢?
在后面的处理中我们可以看到,其实对于root hub ,它不需要进行实际的物理传输,linux按照spec上的规定,将它静态放置在内存中,在进行相关操作的时候,只要直接copy过去就可以了.
其次,要注意,这个函数不能用于中断上下文,因为该函数是同步的,会引起睡眠.
在这里,我们看到,流程最终转入到了下面的代码片段中:
    //如果是root hub
    if (is_root_hub(urb->dev))
        status = rh_urb_enqueue(hcd, urb);
    else
        //如果是一般的设备 
        status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
下面,就分情况来剖析,各种传输到底是怎么完成的.

(转)linux设备驱动之USB数据传输分析 一的更多相关文章

  1. (转)linux设备驱动之USB数据传输分析 二

    3.2:控制传输过程1:root hub的控制传输在前面看到,对于root hub的情况,流程会转入rh_urb_enqueue().代码如下:static int rh_urb_enqueue (s ...

  2. linux设备驱动之USB主机控制器驱动分析 【转】

    转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html   ---------------------------------------- ...

  3. Linux设备驱动之USB

    Linux驱动框架分析(一)        事实上,Linux的设备驱动都遵循一个惯例——表征驱动程序(用driver更贴切一些,应该称为驱动器比较好吧)的结构体,结构体里面应该包含了驱动程序所需要的 ...

  4. 乾坤合一~Linux设备驱动之USB主机和设备驱动

    如果不能陪你到最后 是否后悔当初我们牵手 如果当初没能遇见你 现在的我 在哪里逗留 所有的爱都是冒险 那就心甘情愿 等待我们一生中 所有悬念 我一往情深的恋人 她是我的爱人 她给我的爱就像是 带着露水 ...

  5. (转)Linux设备驱动之HID驱动 源码分析

    //Linux设备驱动之HID驱动 源码分析 http://blog.chinaunix.net/uid-20543183-id-1930836.html HID是Human Interface De ...

  6. Linux 设备驱动 Edition 3

    原文网址:http://oss.org.cn/kernel-book/ldd3/index.html Linux 设备驱动 Edition 3 By Jonathan Corbet, Alessand ...

  7. linux 设备驱动概述

    linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer):       主要利用C库函数和 ...

  8. Linux设备驱动中的软件架构思想

    目录 更新记录 一.Linux驱动的软件架构 1.1 出发点 1.2 分离思想 1.3 分层思想 二.platform设备驱动 2.1 platform设备 2.2 platform驱动 2.3 pl ...

  9. linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-119723.html linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟 xxxxxxxxxx ...

随机推荐

  1. 2017.3.31 spring mvc教程(三)拦截器

    学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...

  2. 显式启动Activity和隐式启动Activity

    1.显式启动Intent intent = new Intent(this, class);startActivity(intent); 2.隐式启动AndroidManifest.xml中定义某个A ...

  3. Python命令行选项參数解析策略

    概述 在Python的项目开发过程中,我们有时须要为程序提供一些能够通过命令行进行调用的接口.只是,并非直接使用 command + 当前文件 就ok的,我们须要对其设置可选的各种各样的操作类型.所以 ...

  4. J2EE环境安装配置

    在下载,安装前先说下以下几个概念JDK,SDK,JRE,JVM ◆JDK Java Develop Kit (Java 开发包) ◆SDK Software Develop kit, 曾经JDK叫做J ...

  5. Python的Django框架中的Cookie相关处理

    Python的Django框架中的Cookie相关处理 浏览器的开发人员在非常早的时候就已经意识到. HTTP's 的无状态会对Web开发人员带来非常大的问题,于是(cookies)应运而生. coo ...

  6. 【VBA】快速填充单元格

    在Excle中,需要填充单元格,直接下拉,然后即可填充,但是使用VBA代码又该如何实现这个呢? 代码区域 Public Sub 快速填充() Dim myRange As range Cells.Cl ...

  7. linux路由服务

    本文介绍怎样使用linux创建一台简单的路由server. 主要包含几个參数的设置:ip_forward和rp_filter. 1.开启IP forwarding # 重新启动后失效 $ echo & ...

  8. Cent OS安装My Sql

    因为公司的需要,所以就自己学习了一下在Linux上安装MySQL,但是翻查了好多博客,没有特别清楚,自己写下来好好总结一下 一.系统环境 CentOS-6.3-i386-bin-DVD1 二.下载My ...

  9. 【SpringMVC学习08】SpringMVC中实现文件上传

    之前有写过一篇struts2实现的文件上传,这一篇博文主要来总结下springmvc实现文件上传的步骤.首先来看一下单个文件的上传,然后再来总结下多个文件上传. 1. 环境准备 springmvc上传 ...

  10. centos安装postgresql

    #安装postgresqlyum -y install postgresql-server #执行数据库初始化脚本service postgresql-9.2 initdb #启动服务service ...