内核中的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, 大概捋一遍,  实践中慢慢用起来...

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值