linux內核有很多優秀的代碼...
比如list
這是一個雙向鏈表。
先貼個好文章...
https://myao0730.blogspot.com/2016/12/linux.html
先看下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, 大概捋一遍, 實踐中慢慢用起來...