usb端点ep_enable与ep_disable
2025-09-26
本文链接为:http://www.usbzh.com/article/detail-1590.html ,欢迎转载,转载请附上本文链接。
启用设备之后,会调用SET_INTERFACE,会在SET_INTERFACE对应的回调function.set_alt,这时会调用usb_ep_enable启用端点。
usb_ep_enable的的结构体信息struct usb_ep来原于usb_ep_autoconfig,该函数的功能是通过输入的端点信息在UDC控制器的端点链表中查找一个空闲的端点,用来分配出来。所以struct usb_ep是一个由UDC控制器提供的信息元。
usb_ep_enable用于启用端点,它其实调用的就是其对应的OP函数,通过源代码也可知。
static inline int usb_ep_enable(struct usb_ep *ep)
{
return ep->ops->enable(ep, ep->desc);
}
static inline int usb_ep_disable(struct usb_ep *ep)
{
return ep->ops->disable(ep);
}
static inline int usb_ep_queue(struct usb_ep *ep,struct usb_request *req, gfp_t gfp_flags)
{
return ep->ops->queue(ep, req, gfp_flags);
}
static inline int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
return ep->ops->dequeue(ep, req);
}
在IMX6ULL中,其UDC的驱动位于\drivers\usb\chipidea
目录下。在其udc.c文件中,提供了回调函数实现。
static const struct usb_ep_ops usb_ep_ops = {
.enable = ep_enable,
.disable = ep_disable,
.alloc_request = ep_alloc_request,
.free_request = ep_free_request,
.queue = ep_queue,//usb_ep_queue
.dequeue = ep_dequeue,//usb_ep_dequeue
.set_halt = ep_set_halt,
.set_wedge = ep_set_wedge,
.fifo_flush = ep_fifo_flush,
};
通过端点的初始化函数init_eps,可以看到具体的初始化过程
static int init_eps(struct ci_hdrc *ci)
{
int retval = 0, i, j;
for (i = 0; i < ci->hw_ep_max/2; i++)
for (j = RX; j <= TX; j++) {
int k = i + j * ci->hw_ep_max/2;
struct ci_hw_ep *hwep = &ci->ci_hw_ep[k];
hwep->ci = ci;
hwep->lock = &ci->lock;
hwep->td_pool = ci->td_pool;
hwep->ep.name = hwep->name;
hwep->ep.ops = &usb_ep_ops;
...
list_add_tail(&hwep->ep.ep_list, &ci->gadget.ep_list);
}
return retval;
}
这里可以看到,所有端点都位于ci->gadget.ep_list队列中。
ep_enable
static int ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
int retval = 0;
unsigned long flags;
u32 cap = 0;
if (ep == NULL || desc == NULL)
return -EINVAL;
spin_lock_irqsave(hwep->lock, flags);
/* only internal SW should enable ctrl endpts */
if (!list_empty(&hwep->qh.queue)) {
dev_warn(hwep->ci->dev, "enabling a non-empty endpoint!\n");
spin_unlock_irqrestore(hwep->lock, flags);
return -EBUSY;
}
hwep->ep.desc = desc;
hwep->dir = usb_endpoint_dir_in(desc) ? TX : RX;
hwep->num = usb_endpoint_num(desc);
hwep->type = usb_endpoint_type(desc);
hwep->ep.maxpacket = usb_endpoint_maxp(desc) & 0x07ff;
hwep->ep.mult = QH_ISO_MULT(usb_endpoint_maxp(desc));
if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
cap |= QH_IOS;
cap |= QH_ZLT;
cap |= (hwep->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT;
/*
* For ISO-TX, we set mult at QH as the largest value, and use
* MultO at TD as real mult value.
*/
if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX)
cap |= 3 << __ffs(QH_MULT);
hwep->qh.ptr->cap = cpu_to_le32(cap);
hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */
if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) {
dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n");
retval = -EINVAL;
}
/*
* Enable endpoints in the HW other than ep0 as ep0
* is always enabled
*/
if (hwep->num)
retval |= hw_ep_enable(hwep->ci, hwep->num, hwep->dir,hwep->type);
spin_unlock_irqrestore(hwep->lock, flags);
return retval;
}
这里看到,要使用端点的时候,有判断端点中的请求队列是否为空,如果为非空,则退出。
ep_disable
static int ep_disable(struct usb_ep *ep)
{
struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
int direction, retval = 0;
unsigned long flags;
if (ep == NULL)
return -EINVAL;
else if (hwep->ep.desc == NULL)
return -EBUSY;
spin_lock_irqsave(hwep->lock, flags);
if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
spin_unlock_irqrestore(hwep->lock, flags);
return 0;
}
/* only internal SW should disable ctrl endpts */
direction = hwep->dir;
do {
retval |= _ep_nuke(hwep);
retval |= hw_ep_disable(hwep->ci, hwep->num, hwep->dir);
if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
hwep->dir = (hwep->dir == TX) ? RX : TX;
} while (hwep->dir != direction);
hwep->ep.desc = NULL;
spin_unlock_irqrestore(hwep->lock, flags);
return retval;
}
_ep_nuke
函数会清除该端点的所有请求队列。
/**
* _ep_nuke: dequeues all endpoint requests
* @hwep: endpoint
*
* This function returns an error code
* Caller must hold lock
*/
static int _ep_nuke(struct ci_hw_ep *hwep)
__releases(hwep->lock)
__acquires(hwep->lock)
{
struct td_node *node, *tmpnode;
if (hwep == NULL)
return -EINVAL;
hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
//从该端点队列中取出所有的请求
while (!list_empty(&hwep->qh.queue)) {
/* pop oldest request */
struct ci_hw_req *hwreq = list_entry(hwep->qh.queue.next,struct ci_hw_req, queue);
list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
dma_pool_free(hwep->td_pool, node->ptr, node->dma);
list_del_init(&node->td);
node->ptr = NULL;
kfree(node);
}
list_del_init(&hwreq->queue); //从队列中移除请求
hwreq->req.status = -ESHUTDOWN; //设置状态
//调用完成例程
if (hwreq->req.complete != NULL) {
spin_unlock(hwep->lock);
usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
spin_lock(hwep->lock);
}
}
if (hwep->pending_td)
free_pending_td(hwep);
return 0;
}
本文链接为:http://www.usbzh.com/article/detail-1590.html ,欢迎转载,转载请附上本文链接。