【智能家居篇】wifi驱动的理解(3)

转载请注明出处: 谢谢!

上一篇文章已经提到USB接口在wifi模块中的最重要两个函数是usb_read_port()和usb_write_port()。那它们是怎么和wifi扯上关系的呢?我们可以从以下三个方面去分析:

1、首先需要明确wifi模块是USB设备,主控(CPU)端是USB主机;

2、USB主机若需要对wifi模块进行数据的读写时,就必须经过USB接口;

3、既然涉及到数据的读写操作,必然要用相应的读写函数,那么usb_read_port()和usb_write_port()即是它们的读写函数。

我们先从读数据开始进行分析,在分析之前,我们必须了解USB设备驱动的读数据过程。USB读取数据操作流程如下:

(1)通过usb_alloc_urb()函数创建并分配一个URB,作为传输USB数据的载体;

(2)创建并分配DMA缓冲区,以DMA方式快速传输数据;

(3)初始化URB,根据wifi的传输数据量,我们需要初始化为批量URB,相应操作函数为usb_fill_bulk_urb();

(4)将URB提交到USB核心;

(5)提交成功后,URB的完成函数将被USB核心调用。

现在我们一步步地详细分析整个过程,所谓的创建和分配,实质上是对内存的分配。作为一名Linux驱动开发程序员,必须了解Linux内存管理相关知识及合理使用内存。

那么我们应该怎样合理地创建和分配URB和DMA缓冲区呢?很明显,我们应该在用的时候分配,在不用的时候释放。

那么问题来了……什么时候在用,又什么时候不用呢?问题很简单,就是主控端读数据时分配,读完后释放,而只有当wifi模块有数据可读时,主控端才能成功地读取数据。那么wifi模块什么时候有数据可读呢?——下面重点来了!wifi模块通过RF端接收到无线网络数据,然后缓存到wifi芯片的RAM中,此时,wifi模块就有数据可读了。

经过上面的分析,我们找到了一条USB接口与wifi模块扯上关系的线索,就是wifi模块的接收数据,会引发USB接口的读数据;

现在,我们转到wifi模块的接收函数中,看看是不是真的这样?

在wifi接收函数初始化中,我们可以看到usb_alloc_urb()创建一个中断URB。伪代码如下:

int xxxwifi_init_recv(_adapter *padapter){struct recv_priv *precvpriv = &padapter->recvpriv;int i, res = _SUCCESS;struct recv_buf *precvbuf;tasklet_init(&precvpriv->recv_tasklet, (void(*)(unsigned long))rtl8188eu_recv_tasklet, (unsigned long)padapter);precvpriv->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); //创建一个中断URBprecvpriv->int_in_buf = rtw_zmalloc(INTERRUPT_MSG_FORMAT_LEN);//init recv_buf_rtw_init_queue(&precvpriv->free_recv_buf_queue);_rtw_init_queue(&precvpriv->recv_buf_pending_queue);precvpriv -> pallocated_recv_buf = rtw_zmalloc(NR_RECVBUFF *sizeof(struct recv_buf) + 4);precvbuf = (struct recv_buf*)precvpriv->precv_buf;for(i=0; i < NR_RECVBUFF ; i++){_rtw_init_listhead(&precvbuf->list);_rtw_spinlock_init(&precvbuf->recvbuf_lock);precvbuf->alloc_sz = MAX_RECVBUF_SZ;res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf);precvbuf->ref_cnt = 0;precvbuf->adapter =padapter;precvbuf++;}precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF;skb_queue_head_init(&precvpriv->rx_skb_queue);#ifdef CONFIG_PREALLOC_RECV_SKB{int i;SIZE_PTR tmpaddr=0;SIZE_PTR alignment=0;struct sk_buff *pskb=NULL;skb_queue_head_init(&precvpriv->free_recv_skb_queue);for(i=0; i<NR_PREALLOC_RECV_SKB; i++){pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);if(pskb){pskb->dev = padapter->pnetdev;tmpaddr = (SIZE_PTR)pskb->data;alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);skb_reserve(pskb, (RECVBUFF_ALIGN_SZ – alignment));skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);}pskb=NULL;}}#endifreturn res;}

在rtw_os_recvbuf_resource_alloc函数中,创建一个批量URB和一个DMA缓冲区。伪代码如下:

int rtw_os_recvbuf_resource_alloc(_adapter *padapter, struct recv_buf *precvbuf){int res=_SUCCESS;struct dvobj_priv*pdvobjpriv = adapter_to_dvobj(padapter);struct usb_device*pusbd = pdvobjpriv->pusbdev;precvbuf->irp_pending = _FALSE;precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); //创建一个批量URBprecvbuf->pskb = NULL;precvbuf->reuse = _FALSE;precvbuf->pallocated_buf = precvbuf->pbuf = NULL;precvbuf->pdata = precvbuf->phead = precvbuf->ptail = precvbuf->pend = NULL;precvbuf->transfer_len = 0;precvbuf->len = 0;#ifdef CONFIG_USE_USB_BUFFER_ALLOC_RXprecvbuf->pallocated_buf = rtw_usb_buffer_alloc(pusbd, (size_t)precvbuf->alloc_sz, &precvbuf->dma_transfer_addr); //创建一个DMA缓冲区precvbuf->pbuf = precvbuf->pallocated_buf;if(precvbuf->pallocated_buf == NULL)return _FAIL;#endif //CONFIG_USE_USB_BUFFER_ALLOC_RXreturn res;}

在usb_read_port()函数中,通过usb_fill_bulk_urb()初始化批量URB,并且提交给USB核心,也即USB读取数据操作流程的第3、4步。在usb_fill_bulk_urb()函数中,初始化URB的完成函数usb_read_port_complete(),只有当URB提交完成后,函数usb_read_port_complete()将被调用。伪代码如下:

static u32 usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem){struct recv_buf*precvbuf = (struct recv_buf *)rmem;_adapter*adapter = pintfhdl->padapter;struct dvobj_priv*pdvobj = adapter_to_dvobj(adapter);struct pwrctrl_priv *pwrctl = dvobj_to_pwrctl(pdvobj);struct recv_priv*precvpriv = &adapter->recvpriv;struct usb_device*pusbd = pdvobj->pusbdev;rtl8188eu_init_recvbuf(adapter, precvbuf);precvpriv->rx_pending_cnt++;purb = precvbuf->purb;//translate DMA FIFO addr to pipehandlepipe = ffaddr2pipehdl(pdvobj, addr);usb_fill_bulk_urb(purb, pusbd, pipe,precvbuf->pbuf,MAX_RECVBUF_SZ,usb_read_port_complete,precvbuf);//context is precvbuferr = usb_submit_urb(purb, GFP_ATOMIC);return ret;}

通过上面的代码,,我们可以得知在wifi模块为接收数据做初始化准备时,分配了URB和DMA缓冲区。而在usb_read_port()函数中初始化URB和提交URB。

它为什么要这样做呢?目的是什么?

接下来,我们将在下一篇文章中为大家揭晓。

转载请注明出处: 谢谢!

让我们将事前的忧虑,换为事前的思考和计划吧!

【智能家居篇】wifi驱动的理解(3)

相关文章:

你感兴趣的文章:

标签云: