本文共 18212 字,大约阅读时间需要 60 分钟。
linux内核有很多优秀的代码...
比如list
这是一个双向链表。
先贴个好文章...
先看下list_head的结构体,
/* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { › struct list_head *next, *prev; };
非常简单, 就记录了两个指针, 一个指向前面节点, 一个指向后面节点。
以dwc3驱动为例, 看下list如何使用。
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total){ struct dwc3_ep *dep; u8 epnum; INIT_LIST_HEAD(&dwc->gadget.ep_list);
初始化端点时, 初始化了一个list_head, 叫ep_list
看下INIT_LIST_HEAD宏,
25 static inline void INIT_LIST_HEAD(struct list_head *list) 26 { 27 › list->next = list; 28 › list->prev = list; 29 }
即该list_head的prev, next指针都指向自己。
也就是说这个list目前是空的。
端点初始化函数中, 继续往下翻,
if (num == 0) { usb_ep_set_maxpacket_limit(&dep->endpoint, 512); dep->endpoint.maxburst = 1; dep->endpoint.ops = &dwc3_gadget_ep0_ops; if (!direction) dwc->gadget.ep0 = &dep->endpoint; } else if (direction) { int mdwidth; int kbytes; int size; int ret; mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0); /* MDWIDTH is represented in bits, we need it in bytes */ mdwidth /= 8; size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num)); size = DWC3_GTXFIFOSIZ_TXFDEF(size); /* FIFO Depth is in MDWDITH bytes. Multiply */ size *= mdwidth; kbytes = size / 1024; if (kbytes == 0) kbytes = 1; /* * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for * internal overhead. We don't really know how these are used, * but documentation say it exists. */ size -= mdwidth * (kbytes + 1); size /= kbytes; usb_ep_set_maxpacket_limit(&dep->endpoint, size); dep->endpoint.max_streams = 15; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, &dwc->gadget.ep_list); ret = dwc3_alloc_trb_pool(dep); if (ret) return ret; } else { int ret; usb_ep_set_maxpacket_limit(&dep->endpoint, 1024); dep->endpoint.max_streams = 15; dep->endpoint.ops = &dwc3_gadget_ep_ops; list_add_tail(&dep->endpoint.ep_list, &dwc->gadget.ep_list); ret = dwc3_alloc_trb_pool(dep); if (ret) return ret; }
这里有些逻辑, 有两处list_add_tail, 总的来说就是描述好IN, OUT的端点, 然后放入gadget.ep_list中。
其中由于ep0是一个双向的端点, 所以需要两个端点物理资源。
那合适会用到这些端点呢?
很好理解, 作为主机端, 肯定是不需要的, 这些端点初始化函数都不会走到。
作为设备端, 当用户具体配置设备时, 就需要使用端点了。
比如配置一个msc storage设备, 肯定需要一个bulk in, 一个bulk out两个端点。以及默认的控制端点。
如果配置一个虚拟串口驱动, 除了控制端点,还需要bulk in, bulk out和一个interrupt中断端点。
所以最终调用的地方有, usb_ep_autoconfig_ss函数。
67 struct usb_ep *usb_ep_autoconfig_ss( 68 › struct usb_gadget› › *gadget, 69 › struct usb_endpoint_descriptor› *desc, 70 › struct usb_ss_ep_comp_descriptor *ep_comp ...... 116 › /* Second, look at endpoints until an unclaimed one looks usable */ 117 › list_for_each_entry (ep, &gadget->ep_list, ep_list) { + 118 // printk("(%d) ep->claimed = %d\n", count++, ep->claimed); 119 › › if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp)) 120 › › › goto found_ep; 121 › }
list_for_each_entry, 看下函数原型
440 /** 441 * list_for_each_entry› -› iterate over list of given type 442 * @pos:› the type * to use as a loop cursor. 443 * @head:› the head for your list. 444 * @member:›the name of the list_head within the struct. 445 */ 446 #define list_for_each_entry(pos, head, member)› › › › \ 447 › for (pos = list_first_entry(head, typeof(*pos), member);› \ 448 › &pos->member != (head);› › › › › \ 449 › pos = list_next_entry(pos, member))
即遍历gadget->ep_list, 然后usb_gadget_ep_match_desc函数进行需求匹配, 如果符号要求, 就会拿出来使用。
总结下list的使用相关。
1. list_head 结构本身很简单, 只记录了prev和next指针。
2. INIT_LIST_HEAD(&dwc->gadget.ep_list); 初始化好一个空队列。队列头为ep_list。
3. list_add_tail,
67 /** 68 * list_add_tail - add a new entry 69 * @new: new entry to be added 70 * @head: list head to add it before 71 * 72 * Insert a new entry before the specified head. 73 * This is useful for implementing queues. 74 */ 75 static inline void list_add_tail(struct list_head *new, struct list_head *head) 76 { 77 › __list_add(new, head->prev, head); 78 }
将前者new, 放入后者队列的尾部。 由于list是一个循环队列。。。
__list_add(new, head->prev, head);
head->prev即为队列的尾部, head是本身, 即头部。
放在尾部和头部之间, 那就是新的尾部。
4. list_add
53 /** 54 * list_add - add a new entry 55 * @new: new entry to be added 56 * @head: list head to add it after 57 * 58 * Insert a new entry after the specified head. 59 * This is good for implementing stacks. 60 */ 61 static inline void list_add(struct list_head *new, struct list_head *head) 62 { 63 › __list_add(new, head, head->next); 64 }
__list_add(new, head, head->next);
即放在head和head->next之间, 就是放在list的头部。 (list_head后面一个)
5. list_entry
345 /** 346 * list_entry - get the struct for this entry 347 * @ptr:› the &struct list_head pointer. 348 * @type:› the type of the struct this is embedded in. 349 * @member:›the name of the list_head within the struct. 350 */ 351 #define list_entry(ptr, type, member) \ 352 › container_of(ptr, type, member)
get the struct for this entry
获取这个list_head的父亲结构体指针。
是的, 只有队列头部是用的一个list_head。
185 /* buffer for one video frame */ 186 struct vivi_buffer { 187 › /* common v4l buffer stuff -- must be first */ 188 › struct vb2_buffer› vb; 189 › struct list_head› list; 190 }; 677 static void vivi_thread_tick(struct vivi_dev *dev) 678 { 679 › struct vivi_dmaqueue *dma_q = &dev->vidq; 680 › struct vivi_buffer *buf; 681 › unsigned long flags = 0; 682 683 › dprintk(dev, 1, "Thread tick\n"); 684 685 › spin_lock_irqsave(&dev->slock, flags); 686 › if (list_empty(&dma_q->active)) { 687 › › dprintk(dev, 1, "No active queue to serve\n"); 688 › › spin_unlock_irqrestore(&dev->slock, flags); 689 › › return; 690 › } 691 692 › buf = list_entry(dma_q->active.next, struct vivi_buffer, list); 693 › list_del(&buf->list); 694 › spin_unlock_irqrestore(&dev->slock, flags); 695 696 › v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); 697 698 › /* Fill buffer */ 699 › vivi_fillbuff(dev, buf); 700 › dprintk(dev, 1, "filled buffer %p\n", buf); 701 702 › vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); 703 › dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); 704 }
比如vivi驱动中, list_entry获取active队列数据头部, 即active.next的父亲结构体指针。 即vivi_buffer的数据指针。
方法呢??
是的, 就是那遍布内核各种驱动的神奇的container_of函数...
6. list_for_each_entry
440 /** 441 * list_for_each_entry› -› iterate over list of given type 442 * @pos:› the type * to use as a loop cursor. 443 * @head:› the head for your list. 444 * @member:›the name of the list_head within the struct. 445 */ 446 #define list_for_each_entry(pos, head, member)› › › › \ 447 › for (pos = list_first_entry(head, typeof(*pos), member);› \ 448 › &pos->member != (head);› › › › › \ 449 › pos = list_next_entry(pos, member))
即遍历队列中所有的数据, 并返还给pos。
看下list_first_entry(肯定又是container_of)
/** * list_first_entry - get the first element from a list * @ptr:› the list head to take the element from. * @type:› the type of the struct this is embedded in. * @member:›the name of the list_struct within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ › list_entry((ptr)->next, type, member)
ok, 大概捋一遍, 实践中慢慢用起来...
转载地址:http://ipcrj.baihongyu.com/