內核中的list相關

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, 大概捋一遍,  實踐中慢慢用起來...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章