• (转)linux设备驱动之USB数据传输分析 一


    三:传输过程的实现
    说到传输过程,我们必须要从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) ",
                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);
    下面,就分情况来剖析,各种传输到底是怎么完成的.

  • 相关阅读:
    天阳科技宁波银行面试题【杭州多测师】【杭州多测师_王sir】
    Python题目:给出一个整型数组 numbers 和一个目标值 target,请在数组中找出两个加起来等于目标值的数的下标【杭州多测师】【杭州多测师_王sir】
    经典面试题:负责的模块,针对这些功能点你是怎么设计测试用例的?【杭州多测师】【杭州多测师_王sir】
    贷款知识面试题【杭州多测师】【杭州多测师_王sir】
    保险知识面试题【杭州多测师】【杭州多测师_王sir】
    Python笔试题:比较数组a和b 并返回相同和不同元素个数【杭州多测师】【杭州多测师_王sir】
    python笔试题:找出两个列表中相同和不同的元素(使用推导式)【杭州多测师】【杭州多测师_王sir】
    经典Python题目:一个列表或者数组去重【杭州多测师】【杭州多测师_王sir】
    想iframe嵌入页面注入css
    Pandas:删除最后一行
  • 原文地址:https://www.cnblogs.com/yihujiu/p/6393684.html
Copyright © 2020-2023  润新知