usb enqueue dequeue

 

参考文档
https://www.cnblogs.com/wen123456/p/14373713.html

https://blog.csdn.net/weixin_42129680/article/details/125949162?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171618781116800182747485%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171618781116800182747485&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-4-125949162-null-null.142^v100^pc_search_result_base6&utm_term=usb_ep_queue&spm=1018.2226.3001.4187

数据包传输:应用层-内核-硬件_应用层读取gadget 数据-CSDN博客

usb设备控制器之uvc数据传输底层实现_usb uvc请求命令-CSDN博客

ioctl—>/drivers/usb/gadget/function/uvc_v4l2.c:v4l2_file_operations uvc_v4l2_fops—>video_ioctl2(该函数在/drivers/media/v4l2-core/v4l2-ioctl.c)—>video_usercopy—>__video_do_ioctl—>v4l2_ioctls(这是个数组,找到对应的cmd执行对应的ioctl)—>v4l_qbuf(入队ioctl)—>ops->vidioc_qbuf(file, fh, p)—>uvc_v4l2_qbuf—>uvcg_video_pump—>uvcg_video_ep_queue—>usb_ep_queue—>ep->ops->queue(ep, req, gfp_flags)—>dwc3_gadget_ep_queue—>__dwc3_gadget_ep_queue(相关dma数据拷贝操作之后调用后面函数使能发送)—>__dwc3_gadget_kick_transfer

                        
原文链接:https://blog.csdn.net/qq_18804879/article/details/118333486

 

static int
uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
        struct video_device *vdev = video_devdata(file);
        struct uvc_device *uvc = video_get_drvdata(vdev);
        struct uvc_video *video = &uvc->video;
        int ret;

        ret = uvcg_queue_buffer(&video->queue, b);
        if (ret < 0)
                return ret;

        return uvcg_video_pump(video);
}

 

/*
 * uvcg_video_pump - Pump video data into the USB requests
 *
 * This function fills the available USB requests (listed in req_free) with
 * video data from the queued buffers.
 */
int uvcg_video_pump(struct uvc_video *video)
{
        struct uvc_video_queue *queue = &video->queue;
        struct usb_request *req;
        struct uvc_buffer *buf;
        unsigned long flags;
        int ret;

        /* FIXME TODO Race between uvcg_video_pump and requests completion
         * handler ???
         */

        while (1) {
                /* Retrieve the first available USB request, protected by the
                 * request lock.
                 */
                spin_lock_irqsave(&video->req_lock, flags);
                if (list_empty(&video->req_free)) {
                        spin_unlock_irqrestore(&video->req_lock, flags);
                        return 0;
                }
                req = list_first_entry(&video->req_free, struct usb_request,
                                        list);
                list_del(&req->list);
                spin_unlock_irqrestore(&video->req_lock, flags);

                /* Retrieve the first available video buffer and fill the
                 * request, protected by the video queue irqlock.
                 */
                spin_lock_irqsave(&queue->irqlock, flags);
                buf = uvcg_queue_head(queue);
                if (buf == NULL) {
                        spin_unlock_irqrestore(&queue->irqlock, flags);
                        break;
                }

                video->encode(req, video, buf);

                /* Queue the USB request */
                ret = uvcg_video_ep_queue(video, req);
                spin_unlock_irqrestore(&queue->irqlock, flags);

                if (ret < 0) {
                        uvcg_queue_cancel(queue, 0);
                        break;
                }
        }

        spin_lock_irqsave(&video->req_lock, flags);
        list_add_tail(&req->list, &video->req_free);
        spin_unlock_irqrestore(&video->req_lock, flags);
        return 0;
}
int usb_ep_queue(struct usb_ep *ep,
                               struct usb_request *req, gfp_t gfp_flags)
{
        int ret = 0;

        if (WARN_ON_ONCE(!ep->enabled && ep->address)) {
                ret = -ESHUTDOWN;
                goto out;
        }
        //调用dwc3_gadget_ep_queue
        ret = ep->ops->queue(ep, req, gfp_flags);

out:
        trace_usb_ep_queue(ep, req, ret);

        return ret;
}
EXPORT_SYMBOL_GPL(usb_ep_queue);

 

static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
        gfp_t gfp_flags)
{
        struct dwc3_request             *req = to_dwc3_request(request);
        struct dwc3_ep                  *dep = to_dwc3_ep(ep);
        struct dwc3                     *dwc = dep->dwc;

        unsigned long                   flags;

        int                             ret;

        if (dwc3_gadget_is_suspended(dwc))
                return -EAGAIN;

        spin_lock_irqsave(&dwc->lock, flags);
        ret = __dwc3_gadget_ep_queue(dep, req);
        spin_unlock_irqrestore(&dwc->lock, flags);

        return ret;
}

2.6.1.2.向非端点0提交USB请求
usb_ep_queue向非端点0提交USB请求的过程如下图所示,最终通过__dwc3_gadget_ep_queue函数提交。下面分析一下向非端点0提交USB请求的主要工作内容:

设置USB请求的actual、status、direction、epnum和length等字段,对于输出端点,length必须是MaxPacketSize的整数倍。
对USB请求的缓冲区进行流式DMA映射。USB请求缓冲区是Function驱动使用kmalloc等函数分配,DMA不能直接使用,需要进行DMA映射,具体的映射过程在2.7小结介绍。
将提交的USB请求先放到pending_list链表。
调用__dwc3_gadget_kick_transfer函数驱动USB设备控制器发送USB请求。这里有几个特殊情况,图里面没有画出,下面简要说明一下。
bulk和control传输,则直接调用__dwc3_gadget_kick_transfer发送USB请求。
int和isoc传输,需要处理XferNotReady、XferInProgress、Stream Capable Bulk Endpoints等情况。
调用dwc3_prepare_trbs函数遍历pending_list将TRB和request绑定,执行完后pending_list链表上的request都会被放到started_list链表上。该函数会根据DMA缓冲区的使用形式做不同的处理,若DMA支持Scatter-gather,则调用dwc3_prepare_one_trb_sg函数,否则调用dwc3_prepare_one_trb_linear函数,最终都是通过dwc3_prepare_one_trb将TRB和request绑定。
从started_list链表中获取一个USB请求。若端点空闲,将其绑定的TRB DMA地址设置到param0和param1寄存器中,命令设置为DWC3_DEPCMD_STARTTRANSFER,开始传输。若端点忙碌,则将命令设置为DWC3_DEPCMD_UPDATETRANSFER,只更新传输,则不发送本次的USB请求。端点是否忙碌通过DWC3_EP_BUSY标志判断。
若端点空闲,则获取端点资源索引,用于辨别是那一次传输。

                        
原文链接:https://blog.csdn.net/u011037593/article/details/123467147

只需要知道一点 usb设备控制器处理的是一个trb结构体指针,控制器自己分析里面数据启动传输


在ioctl qbuf的时候调用到__dwc3_gadget_ep_queue—>usb_gadget_map_request



int usb_gadget_map_request_by_dev(struct device *dev,
        struct usb_request *req, int is_in)
{
    if (req->length == 0)
        return 0;

    if (req->num_sgs) {
        int     mapped;

        mapped = dma_map_sg(dev, req->sg, req->num_sgs,
                is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
        if (mapped == 0) {
            dev_err(dev, "failed to map SGs\n");
            return -EFAULT;
        }

        req->num_mapped_sgs = mapped;
    } else {
        req->dma = dma_map_single(dev, req->buf, req->length,
                is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);在这映射到dma内存种

        if (dma_mapping_error(dev, req->dma)) {
            dev_err(dev, "failed to map buffer\n");
            return -EFAULT;
        }
    }

    return 0;
}

__dwc3_gadget_ep_queue—>__dwc3_gadget_kick_transfer—>dwc3_prepare_trbs—>dwc3_prepare_one_trb_linear—>dwc3_prepare_one_trb

启动传输

static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
{
        struct dwc3_gadget_ep_cmd_params params;
        struct dwc3_request             *req;
        struct dwc3                     *dwc = dep->dwc;
        int                             starting;
        int                             ret;
        u32                             cmd;

        if (!dwc3_calc_trbs_left(dep))
                return 0;

        starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
        //dma相关准备工作  
        //将数据dma地址赋值给dma指向的传输的trb结构体中的地址变量
        dwc3_prepare_trbs(dep);
        req = next_request(&dep->started_list);
        if (!req) {
                dep->flags |= DWC3_EP_PENDING_REQUEST;
                dbg_event(dep->number, "NO REQ", 0);
                return 0;
        }

        memset(&params, 0, sizeof(params));

        if (starting) {
        //这里将指向trb结构体的dma地址赋值给参数,后面分别写进两个寄存器中
        //控制器通过该地址拿到直接存储数据的dma地址以及数据长度,一包数据调用一次
                params.param0 = upper_32_bits(req->trb_dma);
                params.param1 = lower_32_bits(req->trb_dma);
                cmd = DWC3_DEPCMD_STARTTRANSFER;

                if (dep->stream_capable)
                        cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id);

                if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
                        cmd |= DWC3_DEPCMD_PARAM(dep->frame_number);
        } else {
                cmd = DWC3_DEPCMD_UPDATETRANSFER |
                        DWC3_DEPCMD_PARAM(dep->resource_index);
        }
         //将dma 地址写入寄存器
        ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
        if (ret < 0) {
                struct dwc3_request *tmp;

                if (ret == -EAGAIN)
                        return ret;

                dbg_log_string("dep:%s cmd failed ret:%d", dep->name, ret);
                dwc3_stop_active_transfer(dep, true, true);

                list_for_each_entry_safe(req, tmp, &dep->started_list, list)
                        dwc3_gadget_move_cancelled_request(req);

                /* If ep isn't started, then there's no end transfer pending */
                if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
                        dwc3_gadget_ep_cleanup_cancelled_requests(dep);

                return ret;
        }

        return 0;
}

 传输函数 准备工作

dwc3_prepare_trbs

 * dwc3_prepare_trbs - setup TRBs from requests
 * @dep: endpoint for which requests are being prepared
 *
 * The function goes through the requests list and sets up TRBs for the
 * transfers. The function returns once there are no more TRBs available or
 * it runs out of requests.
 */
static void dwc3_prepare_trbs(struct dwc3_ep *dep)
{
        struct dwc3_request     *req, *n;

        BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);

        /*
         * We can get in a situation where there's a request in the started list
         * but there weren't enough TRBs to fully kick it in the first time
         * around, so it has been waiting for more TRBs to be freed up.
         *
         * In that case, we should check if we have a request with pending_sgs
         * in the started list and prepare TRBs for that request first,
         * otherwise we will prepare TRBs completely out of order and that will
         * break things.
         */
        list_for_each_entry(req, &dep->started_list, list) {
                if (req->num_pending_sgs > 0)
                        dwc3_prepare_one_trb_sg(dep, req);

                if (!dwc3_calc_trbs_left(dep))
                        return;
        }

        list_for_each_entry_safe(req, n, &dep->pending_list, list) {
                struct dwc3     *dwc = dep->dwc;
                int             ret;

                ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
                                                    dep->direction);
                if (ret)
                        return;

                req->sg                 = req->request.sg;
                req->start_sg           = req->sg;
                req->num_queued_sgs     = 0;
                req->num_pending_sgs    = req->request.num_mapped_sgs;

                if (req->num_pending_sgs > 0)
                        dwc3_prepare_one_trb_sg(dep, req);
                else
                        dwc3_prepare_one_trb_linear(dep, req);

                dbg_ep_map(dep->number, req);
                if (!dwc3_calc_trbs_left(dep))
                        return;
        }
}

 dwc3_prepare_one_trb_sg

static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
                struct dwc3_request *req)
{
        struct scatterlist *sg = req->start_sg;
        struct scatterlist *s;
        int             i;
        unsigned int length = req->request.length;
        unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
        unsigned int rem = length % maxp;
        unsigned int remaining = req->request.num_mapped_sgs
                - req->num_queued_sgs;

        /*
         * If we resume preparing the request, then get the remaining length of
         * the request and resume where we left off.
         */
        for_each_sg(req->request.sg, s, req->num_queued_sgs, i)
                length -= sg_dma_len(s);

        for_each_sg(sg, s, remaining, i) {
                unsigned int trb_length;
                unsigned chain = true;

                trb_length = min_t(unsigned int, length, sg_dma_len(s));

                length -= trb_length;

                /*
                 * IOMMU driver is coalescing the list of sgs which shares a
                 * page boundary into one and giving it to USB driver. With
                 * this the number of sgs mapped is not equal to the number of
                 * sgs passed. So mark the chain bit to false if it isthe last
                 * mapped sg.
                 */
                if ((i == remaining - 1) || !length)
                        chain = false;

                if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
                        struct dwc3     *dwc = dep->dwc;
                        struct dwc3_trb *trb;

                        req->needs_extra_trb = true;

                        /* prepare normal TRB */
                        dwc3_prepare_one_trb(dep, req, trb_length, true, i);

                        /* Now prepare one extra TRB to align transfer size */
                        trb = &dep->trb_pool[dep->trb_enqueue];
                        req->num_trbs++;
                        __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
                                        maxp - rem, false, 1,
                                        req->request.stream_id,
                                        req->request.short_not_ok,
                                        req->request.no_interrupt);
                } else if (req->request.zero && req->request.length &&
                           !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                           !rem && !chain) {
                        struct dwc3     *dwc = dep->dwc;
                        struct dwc3_trb *trb;

                        req->needs_extra_trb = true;

                        /* Prepare normal TRB */
                        dwc3_prepare_one_trb(dep, req, trb_length, true, i);

                        /* Prepare one extra TRB to handle ZLP */
                        trb = &dep->trb_pool[dep->trb_enqueue];
                        req->num_trbs++;
                        __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
                                               !req->direction, 1,
                                               req->request.stream_id,
                                               req->request.short_not_ok,
                                               req->request.no_interrupt);

                        /* Prepare one more TRB to handle MPS alignment */
                        if (!req->direction) {
                                trb = &dep->trb_pool[dep->trb_enqueue];
                                req->num_trbs++;
                                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
                                                       false, 1, req->request.stream_id,
                                                       req->request.short_not_ok,
                                                       req->request.no_interrupt);
                        }
                } else {
                        dwc3_prepare_one_trb(dep, req, trb_length, chain, i);
                }

                /*
                 * There can be a situation where all sgs in sglist are not
                 * queued because of insufficient trb number. To handle this
                 * case, update start_sg to next sg to be queued, so that
                 * we have free trbs we can continue queuing from where we
                 * previously stopped
                 */
                if (chain)
                        req->start_sg = sg_next(s);

                req->num_queued_sgs++;
                req->num_pending_sgs--;

                /*
                 * The number of pending SG entries may not correspond to the
                 * number of mapped SG entries. If all the data are queued, then
                 * don't include unused SG entries.
                 */
                if (length == 0) {
                        req->num_pending_sgs = 0;
                        break;
                }

                if (!dwc3_calc_trbs_left(dep))
                        break;
        }
}

 dwc3_prepare_one_trb_linear

static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                struct dwc3_request *req)
{
        unsigned int length = req->request.length;
        unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
        unsigned int rem = length % maxp;

        if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) {
                struct dwc3     *dwc = dep->dwc;
                struct dwc3_trb *trb;

                req->needs_extra_trb = true;

                /* prepare normal TRB */
                dwc3_prepare_one_trb(dep, req, length, true, 0);

                /* Now prepare one extra TRB to align transfer size */
                trb = &dep->trb_pool[dep->trb_enqueue];
                req->num_trbs++;
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
                                false, 1, req->request.stream_id,
                                req->request.short_not_ok,
                                req->request.no_interrupt);
        } else if (req->request.zero && req->request.length &&
                   !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                   (IS_ALIGNED(req->request.length, maxp))) {
                struct dwc3     *dwc = dep->dwc;
                struct dwc3_trb *trb;

                req->needs_extra_trb = true;

                /* prepare normal TRB */
                dwc3_prepare_one_trb(dep, req, length, true, 0);

                /* Prepare one extra TRB to handle ZLP */
                trb = &dep->trb_pool[dep->trb_enqueue];
                req->num_trbs++;
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
                                !req->direction, 1, req->request.stream_id,
                                req->request.short_not_ok,
                                req->request.no_interrupt);

                /* Prepare one more TRB to handle MPS alignment for OUT */
                if (!req->direction) {
                        trb = &dep->trb_pool[dep->trb_enqueue];
                        req->num_trbs++;
                        __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
                                               false, 1, req->request.stream_id,
                                               req->request.short_not_ok,
                                               req->request.no_interrupt);
                }
        } else {
                dwc3_prepare_one_trb(dep, req, length, false, 0);
        }
}

  dwc3_prepare_one_trb

/**
 * dwc3_prepare_one_trb - setup one TRB from one request
 * @dep: endpoint for which this request is prepared
 * @req: dwc3_request pointer
 * @trb_length: buffer size of the TRB
 * @chain: should this TRB be chained to the next?
 * @node: only for isochronous endpoints. First TRB needs different type.
 */
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
                struct dwc3_request *req, unsigned int trb_length,
                unsigned chain, unsigned node)
{
        struct dwc3_trb         *trb;
        dma_addr_t              dma;
        unsigned                stream_id = req->request.stream_id;
        unsigned                short_not_ok = req->request.short_not_ok;
        unsigned                no_interrupt = req->request.no_interrupt;

        if (req->request.num_sgs > 0)
                dma = sg_dma_address(req->start_sg);
        else
                dma = req->request.dma;

        trb = &dep->trb_pool[dep->trb_enqueue];

        if (!req->trb) {
                dwc3_gadget_move_started_request(req);
                req->trb = trb; //trb虚拟地址
                req->trb_dma = dwc3_trb_dma_offset(dep, trb);
               //dma地址实际指向上面trb结构体
        }

        req->num_trbs++;

        __dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
                        stream_id, short_not_ok, no_interrupt);
}

 __dwc3_prepare_one_trb

static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
                dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
                unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
{
        struct dwc3             *dwc = dep->dwc;
        struct usb_gadget       *gadget = &dwc->gadget;
        enum usb_device_speed   speed = gadget->speed;

        trb->size = DWC3_TRB_SIZE_LENGTH(length);
        //将前面需要传输数据的dma映射地址赋值给trb低32位
        trb->bpl = lower_32_bits(dma);
        trb->bph = upper_32_bits(dma);

        switch (usb_endpoint_type(dep->endpoint.desc)) {
        case USB_ENDPOINT_XFER_CONTROL:
                trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
                break;

        case USB_ENDPOINT_XFER_ISOC:
                if (!node) {
                        trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;

                        /*
                         * USB Specification 2.0 Section 5.9.2 states that: "If
                         * there is only a single transaction in the microframe,
                         * only a DATA0 data packet PID is used.  If there are
                         * two transactions per microframe, DATA1 is used for
                         * the first transaction data packet and DATA0 is used
                         * for the second transaction data packet.  If there are
                         * three transactions per microframe, DATA2 is used for
                         * the first transaction data packet, DATA1 is used for
                         * the second, and DATA0 is used for the third."
                         *
                         * IOW, we should satisfy the following cases:
                         *
                         * 1) length <= maxpacket
                         *      - DATA0
                         *
                         * 2) maxpacket < length <= (2 * maxpacket)
                         *      - DATA1, DATA0
                         *
                         * 3) (2 * maxpacket) < length <= (3 * maxpacket)
                         *      - DATA2, DATA1, DATA0
                         */
                        if (speed == USB_SPEED_HIGH) {
                                struct usb_ep *ep = &dep->endpoint;
                                unsigned int mult = 2;
                                unsigned int maxp = usb_endpoint_maxp(ep->desc);

                                if (length <= (2 * maxp))
                                        mult--;

                                if (length <= maxp)
                                        mult--;

                                trb->size |= DWC3_TRB_SIZE_PCM1(mult);
                        }
                } else {
                        trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
                }

                /* always enable Interrupt on Missed ISOC */
                trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
                break;

        case USB_ENDPOINT_XFER_BULK:
        case USB_ENDPOINT_XFER_INT:
                trb->ctrl = DWC3_TRBCTL_NORMAL;
                break;
        default:
                /*
                 * This is only possible with faulty memory because we
                 * checked it already :)
                 */
                dev_WARN(dwc->dev, "Unknown endpoint type %d\n",
                                usb_endpoint_type(dep->endpoint.desc));
        }

        /*
         * Enable Continue on Short Packet
         * when endpoint is not a stream capable
         */
        if (usb_endpoint_dir_out(dep->endpoint.desc)) {
                if (!dep->stream_capable)
                        trb->ctrl |= DWC3_TRB_CTRL_CSP;

                if (short_not_ok)
                        trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
        }

        if ((!no_interrupt && !chain) ||
                        (dwc3_calc_trbs_left(dep) == 1))
                trb->ctrl |= DWC3_TRB_CTRL_IOC;

        if (chain)
                trb->ctrl |= DWC3_TRB_CTRL_CHN;

        if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
                trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);

        /*
         * As per data book 4.2.3.2TRB Control Bit Rules section
         *
         * The controller autonomously checks the HWO field of a TRB to determine if the
         * entire TRB is valid. Therefore, software must ensure that the rest of the TRB
         * is valid before setting the HWO field to '1'. In most systems, this means that
         * software must update the fourth DWORD of a TRB last.
         *
         * However there is a possibility of CPU re-ordering here which can cause
         * controller to observe the HWO bit set prematurely.
         * Add a write memory barrier to prevent CPU re-ordering.
         */
        wmb();
        trb->ctrl |= DWC3_TRB_CTRL_HWO;

        dwc3_ep_inc_enq(dep);

        trace_dwc3_prepare_trb(dep, trb);
}


使能发送数据

    int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
        struct dwc3_gadget_ep_cmd_params *params)
{
    struct dwc3        *dwc = dep->dwc;
    u32            timeout = 5000;
    u32            reg;

    int            cmd_status = 0;
    int            susphy = false;
    int            ret = -EINVAL;

    /*
     * Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if
     * we're issuing an endpoint command, we must check if
     * GUSB2PHYCFG.SUSPHY bit is set. If it is, then we need to clear it.
     *
     * We will also set SUSPHY bit to what it was before returning as stated
     * by the same section on Synopsys databook.
     */
    if (dwc->gadget.speed <= USB_SPEED_HIGH) {
        reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
        if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
            susphy = true;
            reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
            dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
        }
    }

    if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
        int        needs_wakeup;

        needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
                dwc->link_state == DWC3_LINK_STATE_U2 ||
                dwc->link_state == DWC3_LINK_STATE_U3);

        if (unlikely(needs_wakeup)) {
            ret = __dwc3_gadget_wakeup(dwc);
            dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
                    ret);
        }
    }
    进行dma 映射 讲params->param0地址写入到寄存器

    dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);将dma地址写入寄存器
    dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
    dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);

    dwc3_writel(dep->regs, DWC3_DEPCMD, cmd | DWC3_DEPCMD_CMDACT);使能usb传输
    省略代码。。。
    }


                        
原文链接:https://blog.csdn.net/qq_18804879/article/details/118387147

 

irqreturn_t dwc3_interrupt(int irq, void *_dwc)
{
        struct dwc3     *dwc = _dwc;
        irqreturn_t     ret = IRQ_NONE;
        irqreturn_t     status;

        status = dwc3_check_event_buf(dwc->ev_buf);
        if (status == IRQ_WAKE_THREAD)
                ret = status;

        if (ret == IRQ_WAKE_THREAD)
                //触发中断下半部处理中断的事件
                queue_work(dwc->dwc_wq, &dwc->bh_work);

        return IRQ_HANDLED;
}

dwc3_bh_work

void dwc3_bh_work(struct work_struct *w)
{
        //w = dwc->bh_work
        struct dwc3 *dwc = container_of(w, struct dwc3, bh_work);

        pm_runtime_get_sync(dwc->dev);
        dwc3_thread_interrupt(dwc->irq, dwc->ev_buf);
        pm_runtime_put(dwc->dev);
}

dwc3_thread_interrupt

static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
{
        struct dwc3_event_buffer *evt = _evt;
        struct dwc3 *dwc = evt->dwc;
        unsigned long flags;
        irqreturn_t ret = IRQ_NONE;
        ktime_t start_time;

        start_time = ktime_get();

        local_bh_disable();
        spin_lock_irqsave(&dwc->lock, flags);
        dwc->bh_handled_evt_cnt[dwc->irq_dbg_index] = 0;
        ret = dwc3_process_event_buf(evt);
        spin_unlock_irqrestore(&dwc->lock, flags);
        local_bh_enable();

        dwc->bh_start_time[dwc->bh_dbg_index] = start_time;
        dwc->bh_completion_time[dwc->bh_dbg_index] =
                ktime_to_us(ktime_sub(ktime_get(), start_time));
        dwc->bh_dbg_index = (dwc->bh_dbg_index + 1) % MAX_INTR_STATS;

        return ret;
}

dwc3_process_event_buf

static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
{
        struct dwc3 *dwc = evt->dwc;
        irqreturn_t ret = IRQ_NONE;
        int left;
        u32 reg;

        left = evt->count;

        if (!(evt->flags & DWC3_EVENT_PENDING))
                return IRQ_NONE;

        while (left > 0) {
                union dwc3_event event;

                event.raw = *(u32 *) (evt->cache + evt->lpos);

                dwc3_process_event_entry(dwc, &event);

                if (dwc->err_evt_seen) {
                        /*
                         * if erratic error, skip remaining events
                         * while controller undergoes reset
                         */
                        evt->lpos = (evt->lpos + left) %
                                        DWC3_EVENT_BUFFERS_SIZE;
                        if (dwc3_notify_event(dwc,
                                                DWC3_CONTROLLER_ERROR_EVENT, 0))
                                dwc->err_evt_seen = 0;
                        dwc->retries_on_error++;
                        break;
                }

                /*
                 * FIXME we wrap around correctly to the next entry as
                 * almost all entries are 4 bytes in size. There is one
                 * entry which has 12 bytes which is a regular entry
                 * followed by 8 bytes data. ATM I don't know how
                 * things are organized if we get next to the a
                 * boundary so I worry about that once we try to handle
                 * that.
                 */
                evt->lpos = (evt->lpos + 4) % evt->length;
                left -= 4;
        }

        dwc->bh_handled_evt_cnt[dwc->bh_dbg_index] += (evt->count / 4);
        evt->count = 0;
        ret = IRQ_HANDLED;

        /* Unmask interrupt */
        reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
        reg &= ~DWC3_GEVNTSIZ_INTMASK;
        dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);

        if (dwc->imod_interval) {
                dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
                dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
        }

        /* Keep the clearing of DWC3_EVENT_PENDING at the end */
        evt->flags &= ~DWC3_EVENT_PENDING;

        return ret;
}

dwc3_process_event_entry

static void dwc3_process_event_entry(struct dwc3 *dwc,
                const union dwc3_event *event)
{
        trace_dwc3_event(event->raw, dwc);

        if (!event->type.is_devspec)
                dwc3_endpoint_interrupt(dwc, &event->depevt);
        else if (event->type.type == DWC3_EVENT_TYPE_DEV)
                dwc3_gadget_interrupt(dwc, &event->devt);
        else
                dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw);
}

 

[drivers/usb/dwc3/core.h]
/* dwc3 USB设备端点事件宏定义 */
#define DWC3_DEPEVT_XFERCOMPLETE	0x01  // 传输完成 
#define DWC3_DEPEVT_XFERINPROGRESS	0x02  // 传输正在处理中
#define DWC3_DEPEVT_XFERNOTREADY	0x03  // 传输未准备好
// 接收和发送FIFO事件 (IN->Underrun, OUT->Overrun)
#define DWC3_DEPEVT_RXTXFIFOEVT		0x04  
#define DWC3_DEPEVT_STREAMEVT		0x06  // 流事件
#define DWC3_DEPEVT_EPCMDCMPLT		0x07  // 端点命令完成
struct dwc3_event_depevt {  /* USB设备端点事件 */
	u32	one_bit:1;          // 未使用
	u32	endpoint_number:5;  // 端点编号
	u32	endpoint_event:4;   // 端点事件
	u32	reserved11_10:2;
	u32	status:4;           // 端点事件状态
	u32	parameters:16;
} __packed;


/* dwc3 USB设备控制器事件类型 */
#define DWC3_DEVICE_EVENT_DISCONNECT		    0
#define DWC3_DEVICE_EVENT_RESET			        1
#define DWC3_DEVICE_EVENT_CONNECT_DONE		    2
#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE	3
#define DWC3_DEVICE_EVENT_WAKEUP		        4
#define DWC3_DEVICE_EVENT_HIBER_REQ		        5
#define DWC3_DEVICE_EVENT_EOPF			        6
#define DWC3_DEVICE_EVENT_SOF			        7
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR		    9
#define DWC3_DEVICE_EVENT_CMD_CMPL		        10
#define DWC3_DEVICE_EVENT_OVERFLOW		        11
// VndrDevTstRcved                              12
struct dwc3_event_devt {  /* USB设备事件 */
	u32	one_bit:1;        // 未使用
	u32	device_event:7;   // 设备事件
	u32	type:4;           // 设备事件类型
	u32	reserved15_12:4;
	u32	event_info:9;     // 事件信息
	u32	reserved31_25:7;
} __packed;

/* Other Core Events */
struct dwc3_event_gevt {
	u32	one_bit:1;          // 未使用
	u32	device_event:7;     // (0x03) Carkit or (0x04) I2C event
	u32	phy_port_number:4;  // self-explanatory
	u32	reserved31_12:20;
} __packed;

dwc3_endpoint_interrupt

static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
{
        struct dwc3_ep          *dep;
        u8                      epnum = event->endpoint_number;
        u8                      cmd;

        dep = dwc->eps[epnum];

        if (!(dep->flags & DWC3_EP_ENABLED)) {
                if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
                        return;

                /* Handle only EPCMDCMPLT when EP disabled */
                if (event->endpoint_event != DWC3_DEPEVT_EPCMDCMPLT)
                        return;
        }

        if (epnum == 0 || epnum == 1) {
                dwc3_ep0_interrupt(dwc, event);
                return;
        }

        dep->dbg_ep_events.total++;

        switch (event->endpoint_event) {
        case DWC3_DEPEVT_XFERINPROGRESS:
                dep->dbg_ep_events.xferinprogress++;
                dwc3_gadget_endpoint_transfer_in_progress(dep, event);
                break;
        case DWC3_DEPEVT_XFERNOTREADY:
                dep->dbg_ep_events.xfernotready++;
                dwc3_gadget_endpoint_transfer_not_ready(dep, event);
                break;
        case DWC3_DEPEVT_EPCMDCMPLT:
                dep->dbg_ep_events.epcmdcomplete++;
                cmd = DEPEVT_PARAMETER_CMD(event->parameters);

                if (cmd == DWC3_DEPCMD_ENDTRANSFER &&
                        (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
                        dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
                        dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
                        dwc3_gadget_ep_cleanup_cancelled_requests(dep);
                        dbg_log_string("DWC3_DEPEVT_EPCMDCMPLT (%d)",
                                                        dep->number);

                        if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) {
                                struct dwc3 *dwc = dep->dwc;

                                dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL;
                                if (dwc3_send_clear_stall_ep_cmd(dep)) {
                                        struct usb_ep *ep0 = &dwc->eps[0]->endpoint;

                                        dev_err(dwc->dev, "failed to clear STALL on %s\n",
                                                dep->name);
                                        if (dwc->delayed_status)
                                                __dwc3_gadget_ep0_set_halt(ep0, 1);
                                        return;
                                }

                                dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
                                if (dwc->clear_stall_protocol == dep->number)
                                        dwc3_ep0_send_delayed_status(dwc);
                        }

                        if ((dep->flags & DWC3_EP_DELAY_START) &&
                            !usb_endpoint_xfer_isoc(dep->endpoint.desc))
                                __dwc3_gadget_kick_transfer(dep);

                        dep->flags &= ~DWC3_EP_DELAY_START;
                }
                break;
        case DWC3_DEPEVT_STREAMEVT:
                dep->dbg_ep_events.streamevent++;
                break;
        case DWC3_DEPEVT_XFERCOMPLETE:
                dep->dbg_ep_events.xfercomplete++;
                break;
        case DWC3_DEPEVT_RXTXFIFOEVT:
                dep->dbg_ep_events.rxtxfifoevent++;
                break;
        }
}

dwc3_gadget_endpoint_transfer_in_progress

static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event)
{
        struct dwc3             *dwc = dep->dwc;
        unsigned                status = 0;
        bool                    stop = false;

        dwc3_gadget_endpoint_frame_from_event(dep, event);

        if (event->status & DEPEVT_STATUS_BUSERR)
                status = -ECONNRESET;

        dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);

        if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
                status = -EXDEV;
                dep->missed_isoc_packets++;
                dbg_event(dep->number, "MISSEDISOC", dep->missed_isoc_packets);
        }

        if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                        (list_empty(&dep->started_list))) {
                stop = true;
                dbg_event(dep->number, "STOPXFER", dep->frame_number);
        }

        if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
                goto out;

        if (stop)
                dwc3_stop_active_transfer(dep, true, true);
        else if (dwc3_gadget_ep_should_continue(dep))
                __dwc3_gadget_kick_transfer(dep);

out:
        /*
         * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
         * See dwc3_gadget_linksts_change_interrupt() for 1st half.
         */
        if (dwc->revision < DWC3_REVISION_183A) {
                u32             reg;
                int             i;

                for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
                        dep = dwc->eps[i];

                        if (!(dep->flags & DWC3_EP_ENABLED))
                                continue;

                        if (!list_empty(&dep->started_list))
                                return;
                }

                reg = dwc3_readl(dwc->regs, DWC3_DCTL);
                reg |= dwc->u1u2;
                dwc3_writel(dwc->regs, DWC3_DCTL, reg);

                dwc->u1u2 = 0;
        }
}

dwc3_gadget_ep_cleanup_completed_requests

static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event, int status)
{
        struct dwc3_request     *req;

        while (!list_empty(&dep->started_list)) {
                int ret;

                req = next_request(&dep->started_list);
                ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
                                req, status);
                if (ret)
                        break;
        }
}

 

dwc3_gadget_ep_cleanup_completed_request

static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event,
                struct dwc3_request *req, int status)
{
        struct dwc3 *dwc = dep->dwc;
        int request_status;
        int ret;

        /*
         * If the HWO is set, it implies the TRB is still being
         * processed by the core. Hence do not reclaim it until
         * it is processed by the core.
         */
        if (req->trb->ctrl & DWC3_TRB_CTRL_HWO) {
                dbg_event(0xFF, "PEND TRB", dep->number);
                return 1;
        }

        if (req->request.num_mapped_sgs)
                ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event,
                                status);
        else
                ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
                                status);

        req->request.actual = req->request.length - req->remaining;

        if (!dwc3_gadget_ep_request_completed(req))
                goto out;

        if (req->needs_extra_trb) {
                unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);

                ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
                                status);

                /* Reclaim MPS padding TRB for ZLP */
                if (!req->direction && req->request.zero && req->request.length &&
                    !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                    (IS_ALIGNED(req->request.length, maxp)))
                        ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status);

                req->needs_extra_trb = false;
        }

        /*
         * The event status only reflects the status of the TRB with IOC set.
         * For the requests that don't set interrupt on completion, the driver
         * needs to check and return the status of the completed TRBs associated
         * with the request. Use the status of the last TRB of the request.
         */
        if (req->request.no_interrupt) {
                struct dwc3_trb *trb;

                trb = dwc3_ep_prev_trb(dep, dep->trb_dequeue);
                switch (DWC3_TRB_SIZE_TRBSTS(trb->size)) {
                case DWC3_TRBSTS_MISSED_ISOC:
                        /* Isoc endpoint only */
                        request_status = -EXDEV;
                        break;
                case DWC3_TRB_STS_XFER_IN_PROG:
                        /* Applicable when End Transfer with ForceRM=0 */
                case DWC3_TRBSTS_SETUP_PENDING:
                        /* Control endpoint only */
                case DWC3_TRBSTS_OK:
                default:
                        request_status = 0;
                        break;
                }
        } else {
                request_status = status;
        }

        dwc3_gadget_giveback(dep, req, request_status);

out:
        return ret;
}

dwc3_gadget_giveback

/**
 * dwc3_gadget_giveback - call struct usb_request's ->complete callback
 * @dep: The endpoint to whom the request belongs to
 * @req: The request we're giving back
 * @status: completion code for the request
 *
 * Must be called with controller's lock held and interrupts disabled. This
 * function will unmap @req and call its ->complete() callback to notify upper
 * layers that it has completed.
 */
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
                int status)
{
        struct dwc3                     *dwc = dep->dwc;

        dwc3_gadget_del_and_unmap_request(dep, req, status);
        req->status = DWC3_REQUEST_STATUS_COMPLETED;

        if (dep->endpoint.desc && usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                        (list_empty(&dep->started_list))) {
                dep->flags |= DWC3_EP_PENDING_REQUEST;
                dbg_event(dep->number, "STARTEDLISTEMPTY", 0);
        }

        spin_unlock(&dwc->lock);
        usb_gadget_giveback_request(&dep->endpoint, &req->request);
        spin_lock(&dwc->lock);
}

usb_gadget_giveback_request

/**
 * usb_gadget_giveback_request - give the request back to the gadget layer
 * Context: in_interrupt()
 *
 * This is called by device controller drivers in order to return the
 * completed request back to the gadget layer.
 */
void usb_gadget_giveback_request(struct usb_ep *ep,
                struct usb_request *req)
{
        if (likely(req->status == 0))
                usb_led_activity(USB_LED_EVENT_GADGET);

        trace_usb_gadget_giveback_request(ep, req, 0);

        req->complete(ep, req);//调用对应的function的complete uvc_video_complete
}
EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);

uvc_video_complete 进行循环enqueue  & dequeue buf

*
 * The USB request completion handler will get the buffer at the irqqueue head
 * under protection of the queue spinlock. If the queue is empty, the streaming
 * paused flag will be set. Right after releasing the spinlock a userspace
 * application can queue a buffer. The flag will then cleared, and the ioctl
 * handler will restart the video stream.
 */
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
        struct uvc_video *video = req->context;
        struct uvc_video_queue *queue = &video->queue;
        struct uvc_buffer *buf;
        unsigned long flags;
        int ret;

        switch (req->status) {
        case 0:
                break;

        case -ESHUTDOWN:        /* disconnect from host. */
                uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
                uvcg_queue_cancel(queue, 1);
                goto requeue;

        default:
                uvcg_warn(&video->uvc->func,
                          "VS request completed with status %d.\n",
                          req->status);
                uvcg_queue_cancel(queue, 0);
                goto requeue;
        }

        spin_lock_irqsave(&video->queue.irqlock, flags);
        buf = uvcg_queue_head(&video->queue);
        if (buf == NULL) {
                spin_unlock_irqrestore(&video->queue.irqlock, flags);
                goto requeue;
        }

        video->encode(req, video, buf);

        ret = uvcg_video_ep_queue(video, req);
        spin_unlock_irqrestore(&video->queue.irqlock, flags);

        if (ret < 0) {
                uvcg_queue_cancel(queue, 0);
                goto requeue;
        }

        return;

requeue:
        spin_lock_irqsave(&video->req_lock, flags);
        list_add_tail(&req->list, &video->req_free);
        spin_unlock_irqrestore(&video->req_lock, flags);
}

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值