查看: 1812|回复: 1

[主题月] 主题月——wifi的理解

[复制链接]
  • TA的每日心情
    开心
    2023-2-28 15:37
  • 签到天数: 42 天

    [LV.5]常住居民I

    42

    主题

    500

    帖子

    0

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    1369

    热心会员

    最后登录
    2024-2-23
    发表于 2021-6-17 10:58:12 | 显示全部楼层 |阅读模式
    本帖最后由 y369369 于 2021-6-17 10:59 编辑

    主题月刚好是wifi,所以今天我们来分析分析wifi。首先我们从USB设备这条线索开始。在分析之前,我们需要理解在整个wifi模块中,USB充当什么角色?它的作用是什么?实质上wifi模块上的数据传输有两端,一端是wifi芯片与wifi芯片之间,通过无线射频(RF)进行数据传输;另一端则是wifi芯片与CPU之间,通过USB进行数据传输。
    了解Linux的USB驱动的读者都知道,USB驱动分为两种:一种是USB主机驱动;另一种是USB设备驱动。而我们的USB接口的wifi模块对于CPU(主机)来说,属于USB设备,因此采用USB设备驱动。

    有了以上信息之后,我们先让Linux系统识别该USB接口的wifi模块,首先我们在驱动源码中大致添加以下几步工作:
    (1)定义一个usb_driver结构体变量:
    1. struct usb_driver xxx_usb_wifi_driver;
    复制代码
    (2)填充该设备的usb_driver结构体成员变量:
    1. static struct usb_driver xxx_usb_wifi_driver = {

    2.                      .name =             "XXX_USB_WIFI",

    3.                      .probe =   xxx_init_wifi,

    4.                      .disconnect =   xxx_remove,

    5.                      .suspend =        xxx_suspend,

    6.                      .resume =         xxx_resume,

    7.                      .id_table=        xxx_table,

    8.             };
    复制代码
    (3)将该驱动注册到USB子系统:
         usb_register(&xxx_usb_wifi_driver);
         简单完成以上几步工作,再加上板级文件(arch/mach-xxx.c)对USB设备的支持,Linux的USB子系统几乎可以挂载该wifi模块为USB设备了。但是这并不是我们最终想要的结果。我们还要让Linux系统知道它挂载的USB设备属于无线网络设备,同时能够访问它,利用它实施无线网络的工作。
         我们都知道,若要让USB设备真正工作起来,需要对USB设备的4个层次(设备、配置、接口、端点)进行初始化。当然这四个层次并不是一定都要进行初始化,而是根据你的USB设备的功能进行选择的,大致初始化流程如下伪代码:
    1. static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf)
    2.     {
    3.              int    i;
    4.              u8     val8;
    5.              int    status= _FAIL;
    6.              struct dvobj_priv *pdvobjpriv;

    7.             //设备
    8.              struct usb_device                                *pusbd;
    9.              struct usb_device_descriptor         *pdev_desc;

    10.             //配置
    11.              struct usb_host_config                      *phost_conf;
    12.              struct usb_config_descriptor           *pconf_desc;

    13.              //接口
    14.              struct usb_host_interface                *phost_iface;
    15.              struct usb_interface_descriptor     *piface_desc;

    16.             //端点
    17.              struct usb_host_endpoint                 *phost_endp;
    18.              struct usb_endpoint_descriptor      *pendp_desc;

    19.              //设备的初始化
    20.             pdvobjpriv->pusbintf = usb_intf ;
    21.              pusbd =pdvobjpriv->pusbdev = interface_to_usbdev(usb_intf);
    22.             usb_set_intfdata(usb_intf, pdvobjpriv);
    23.              pdev_desc =&pusbd->descriptor;

    24.              //配置的初始化
    25.              phost_conf =pusbd->actconfig;
    26.              pconf_desc =&phost_conf->desc;

    27.             //接口的初始化
    28.              phost_iface =&usb_intf->altsetting[0];
    29.              piface_desc =&phost_iface->desc;
    复制代码
    端点的初始化,由于wifi模块属于网络设备,传输批量数据,因此需要初始化为批量端点,端点方向(输入、输出)等。同时,由于wifi驱动功能比较多,需要初始化几个输入输出端点。
    1. for (i = 0; i <pdvobjpriv->nr_endpoint; i++)
    2.              {
    3.                       phost_endp = phost_iface->endpoint +i;
    4.                       if (phost_endp)
    5.                       {
    6.                                 pendp_desc =&phost_endp->desc;
    7.                                 //检查是否为输入端点
    8.                                 usb_endpoint_is_bulk_in(pendp_desc);
    9.                                 //检查是否为输出端点
    10.                                 usb_endpoint_is_bulk_out(pendp_desc);
    11.                        }
    12.              }
    13.             usb_get_dev(pusbd);
    14.     }
    复制代码
    完成以上的初始化工作之后,接下来我们需要理清一下USB接口的作用,它是wifi芯片内部的固件程序与主机上的Linux系统进行数据通信。USB设备通信不像普通字符设备那样采用I/O内存和I/O端口的访问,而是采用一种称为URB(USB Request Block)的USB请求块,URB在整个USB子系统中,相当于通电设备中的“电波”,USB主机与设备的通信,通过“电波”来传递。下面我们就来编写USB接口的读写操作函数,伪代码如下:
    1. void xxx_wifi_usb_intf_ops(struct _io_ops     *pops)
    2.      {
    3.               //当需要进行简单数据的读取时,采用以下操作
    4.               pops->_read8 = &usb_read8;
    5.               pops->_read16 = &usb_read16;
    6.               pops->_read32 = &usb_read32;
    7.               //当需要进行批量数据的读取时,采用以下操作
    8.               pops->_read_port = &usb_read_port;
    9.                //当需要进行简单数据的写时,采用以下操作
    10.               pops->_write8 = &usb_write8;
    11.               pops->_write16 = &usb_write16;
    12.               pops->_write32 = &usb_write32;
    13.               pops->_writeN = &usb_writeN;
    14.               //当需要进行批量数据的写时,采用以下操作
    15.               pops->_write_port = &usb_write_port;
    16.               //取消读写urb
    17.               pops->_read_port_cancel = &usb_read_port_cancel;
    18.               pops->_write_port_cancel = &usb_write_port_cancel;
    19.       }
    复制代码
    在进行批量数据的读写时,如usb_read_port()和usb_write_port()函数,需要完成urb创建、初始化、提交、完成处理这个完整的流程。伪代码如下:
    (1)批量读操作
    1. static u32 usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem)
    2.     {
    3.              int err;
    4.              unsigned intpipe;
    5.              PURB purb =NULL;
    6.              structrecv_buf         *precvbuf = (structrecv_buf *)rmem;
    7.              structusb_device    *pusbd = pdvobj->pusbdev;
    8.              //创建urb,这里是在其它地方创建完成之后,传递过来
    9.              purb =precvbuf->purb;
    10.              //初始化批量urb
    11.             usb_fill_bulk_urb(purb, pusbd, pipe,
    12.                                            precvbuf->pbuf,
    13.                                              MAX_RECVBUF_SZ,
    14.                                              usb_read_port_complete,
    15.                                              precvbuf);//contextis precvbuf
    16.              //提交urb
    17.              err =usb_submit_urb(purb, GFP_ATOMIC);
    18.     }
    复制代码
    (2)批量写操作
    1. u32 usb_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem)
    2.      {
    3.              unsigned int pipe;
    4.              intstatus;
    5.              PURB        purb = NULL;
    6.              structxmit_priv       *pxmitpriv =&padapter->xmitpriv;
    7.              structxmit_buf *pxmitbuf = (struct xmit_buf *)wmem;
    8.              structxmit_frame *pxmitframe = (struct xmit_frame *)pxmitbuf->priv_data;
    9.              structusb_device *pusbd = pdvobj->pusbdev;
    10.              structpkt_attrib *pattrib = &pxmitframe->attrib;
    11.              //创建urb,这里是在其它地方创建完成之后,传递过来
    12.              purb = pxmitbuf->pxmit_urb[0];
    13.               //初始化批量urb
    14.              usb_fill_bulk_urb(purb, pusbd, pipe,
    15.                                  pxmitframe->buf_addr,//= pxmitbuf->pbuf
    16.                                  cnt,
    17.                                  usb_write_port_complete,
    18.                                  pxmitbuf);//contextis pxmitbuf
    19.              //提交urb
    20.              status = usb_submit_urb(purb,GFP_ATOMIC);
    21.              return ret;
    22.     }
    复制代码
    完成以上批量数据的读写操作之后,大家可能会疑问:这不是一般USB设备驱动的操作流程吗?貌似和wifi没有半毛钱的关系啊!

    写到这发现确实和wifi没有任何联系,但是以上只是一个铺垫。我们一直强调USB接口在wifi模块中充当什么角色,既然是接口,那么它就是为数据传输而生。所以,和wifi扯上关系的就在于usb_read_port()和usb_write_port()这两个函数。

    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。伪代码如下:
    1. int xxxwifi_init_recv(_adapter *padapter)
    2. {
    3.         struct recv_priv *precvpriv = &padapter->recvpriv;
    4.         int i, res = _SUCCESS;
    5.         struct recv_buf *precvbuf;

    6.         tasklet_init(&precvpriv->recv_tasklet, (void(*)(unsigned long))rtl8188eu_recv_tasklet, (unsigned long)padapter);

    7.         precvpriv->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); //创建一个中断URB

    8.         precvpriv->int_in_buf = rtw_zmalloc(INTERRUPT_MSG_FORMAT_LEN);
    9.         //init recv_buf
    10.         _rtw_init_queue(&precvpriv->free_recv_buf_queue);
    11.         _rtw_init_queue(&precvpriv->recv_buf_pending_queue);

    12.         precvpriv -> pallocated_recv_buf = rtw_zmalloc(NR_RECVBUFF *sizeof(struct recv_buf) + 4);
    13.         precvbuf = (struct recv_buf*)precvpriv->precv_buf;

    14.         for(i=0; i < NR_RECVBUFF ; i++)
    15.         {
    16.                 _rtw_init_listhead(&precvbuf->list);
    17.                 _rtw_spinlock_init(&precvbuf->recvbuf_lock);
    18.                 precvbuf->alloc_sz = MAX_RECVBUF_SZ;

    19.                 res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf);

    20.                 precvbuf->ref_cnt = 0;
    21.                 precvbuf->adapter =padapter;
    22.                 precvbuf++;
    23.         }
    24.         precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF;

    25.         skb_queue_head_init(&precvpriv->rx_skb_queue);

    26. #ifdef CONFIG_PREALLOC_RECV_SKB
    27.         {
    28.                 int i;
    29.                 SIZE_PTR tmpaddr=0;
    30.                 SIZE_PTR alignment=0;
    31.                 struct sk_buff *pskb=NULL;
    32.                 skb_queue_head_init(&precvpriv->free_recv_skb_queue);
    33.                 for(i=0; i<NR_PREALLOC_RECV_SKB; i++)
    34.                 {
    35.                         pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);
    36.                         if(pskb)
    37.                         {
    38.                                 pskb->dev = padapter->pnetdev;
    39.                                 tmpaddr = (SIZE_PTR)pskb->data;
    40.                                 alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
    41.                                 skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment));
    42.                                 skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
    43.                         }
    44.                         pskb=NULL;
    45.                 }
    46.         }
    47. #endif
    48.         return res;
    49. }
    复制代码
      在rtw_os_recvbuf_resource_alloc函数中,创建一个批量URB和一个DMA缓冲区。伪代码如下:
    1. int rtw_os_recvbuf_resource_alloc(_adapter *padapter, struct recv_buf *precvbuf)
    2. {
    3.         int res=_SUCCESS;
    4.         struct dvobj_priv        *pdvobjpriv = adapter_to_dvobj(padapter);
    5.         struct usb_device        *pusbd = pdvobjpriv->pusbdev;

    6.         precvbuf->irp_pending = _FALSE;
    7.         precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); //创建一个批量URB

    8.         precvbuf->pskb = NULL;
    9.         precvbuf->reuse = _FALSE;
    10.         precvbuf->pallocated_buf  = precvbuf->pbuf = NULL;
    11.         precvbuf->pdata = precvbuf->phead = precvbuf->ptail = precvbuf->pend = NULL;
    12.         precvbuf->transfer_len = 0;
    13.         precvbuf->len = 0;

    14.         #ifdef CONFIG_USE_USB_BUFFER_ALLOC_RX
    15.         precvbuf->pallocated_buf = rtw_usb_buffer_alloc(pusbd, (size_t)precvbuf->alloc_sz, &precvbuf->dma_transfer_addr);  //创建一个DMA缓冲区
    16.         precvbuf->pbuf = precvbuf->pallocated_buf;
    17.         if(precvbuf->pallocated_buf == NULL)
    18.                 return _FAIL;
    19.         #endif //CONFIG_USE_USB_BUFFER_ALLOC_RX
    20.         
    21.         return res;
    22. }
    复制代码
    在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()将被调用。伪代码如下:
    1. static u32 usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem)
    2. {        
    3.         struct recv_buf        *precvbuf = (struct recv_buf *)rmem;
    4.         _adapter                *adapter = pintfhdl->padapter;
    5.         struct dvobj_priv        *pdvobj = adapter_to_dvobj(adapter);
    6.         struct pwrctrl_priv *pwrctl = dvobj_to_pwrctl(pdvobj);
    7.         struct recv_priv        *precvpriv = &adapter->recvpriv;
    8.         struct usb_device        *pusbd = pdvobj->pusbdev;

    9.         rtl8188eu_init_recvbuf(adapter, precvbuf);               

    10.         precvpriv->rx_pending_cnt++;

    11.         purb = precvbuf->purb;

    12.         //translate DMA FIFO addr to pipehandle
    13.         pipe = ffaddr2pipehdl(pdvobj, addr);

    14.         usb_fill_bulk_urb(purb, pusbd, pipe,
    15.                                         precvbuf->pbuf,
    16.                                             MAX_RECVBUF_SZ,
    17.                                             usb_read_port_complete,
    18.                                             precvbuf);//context is precvbuf

    19.         err = usb_submit_urb(purb, GFP_ATOMIC);

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

    好了,今天就分享到这,下次我们接着写,欢迎与坛友们交流。









    签到签到
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2022-5-25 16:03
  • 签到天数: 11 天

    [LV.3]偶尔看看II

    8

    主题

    718

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    3395
    最后登录
    2024-3-5
    发表于 2021-6-17 12:28:32 | 显示全部楼层
    如果是非liunx系统怎么办?祼跑没有系统的怎么办?
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-25 15:15 , Processed in 0.119774 second(s), 19 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表