Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析

前面学习了SDIO接口的WiFi驱动,现在我们来学习一下USB接口的WiFi驱动,二者的区别在于接口不同。而USB接口的设备驱动,我们前面也有学习,比如USB摄像头驱动、USB鼠标驱动,同样都符合LinuxUSB驱动结构:


        USB设备驱动(字符设备、块设备、网络设备)

                                               |

                                        USB 核心

                                               |

                              USB主机控制器驱动


        不同之处只是在于USB摄像头驱动是字符设备,而我们今天要学习的WiFi驱动是网络设备;当然由我们编写的部分还是USB设备驱动部分,下面进入USB接口WiFi驱动的分析,如何分析呢?我们下面从这几个方面入手:

        从硬件层面上看,WIFI设备与CPU通信是通过USB接口的与其他WIFI设备之间的通信是通过无线射频(RF)

        从软件层面上看,Linux操作系统要管理WIFI设备,那么就要将WIFI设备挂载到USB总线上,通过USB子系统实现管理。而同时为了对接网络,又将WIFI设备封装成一个网络设备

        我们以USB接口的WIFI模块进行分析:

a -- 从USB总线的角度去看,它是USB设备;

b -- 从Linux设备的分类上看,它又是网络设备;

c -- 从WIFI本身的角度去看,它又有自己独特的功能及属性,因此它又是一个私有的设备;

通过上述的分析,我们只要抓住这三条线索深入去分析它的驱动源码,整个WIFI驱动框架就会浮现在你眼前。


一、框架整理

1、USB设备驱动

      现在我们先从USB设备开始,要写一个USB设备驱动,那么大致步骤如下:

a -- 需要针对该设备定义一个USB驱动,对应到代码中即定义一个usb_driver结构体变量

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct usb_driver xxx_usb_wifi_driver;  

b -- 填充该设备的usb_driver结构体成员变量

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static struct usb_driver xxx_usb_wifi_driver = {  
  2.     .name = "XXX_USB_WIFI",  
  3.     .probe = xxx_probe,  
  4.     .disconnect = xxx_disconnect,  
  5.     .suspend = xxx_suspend,  
  6.     .resume = xxx_resume,  
  7.     .id_table = xxx_table,  
  8. };  

c -- 将该驱动注册到USB子系统

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. usb_register(&xxx_usb_wifi_driver);  

      以上步骤只是一个大致的USB驱动框架流程,而最大和最复杂的工作是填充usb_driver结构体成员变量。以上步骤的主要工作是将USB接口的WIFI设备挂载到USB总线上,以便Linux系统在USB总线上就能够找到该设备。

2、网络设备驱动

      接下来是网络设备的线索,网络设备驱动大致步骤如下:

a -- 定义一个net_device结构体变量ndev

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct net_device *ndev;  

b -- 初始化ndev变量并分配内存

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ndev=alloc_etherdev();  

c -- 填充ndev -> netdev_ops结构体成员变量

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static const struct net_device_ops xxx_netdev_ops= {  
  2.     .ndo_init = xxx_ndev_init,  
  3.     .ndo_uninit = xxx _ndev_uninit,  
  4.     .ndo_open = netdev_open,  
  5.     .ndo_stop = netdev_close,  
  6.     .ndo_start_xmit = xxx_xmit_entry,  
  7.     .ndo_set_mac_address = xxx_net_set_mac_address,  
  8.     .ndo_get_stats = xxx_net_get_stats,  
  9.     .ndo_do_ioctl = xxx_ioctl,  
  10. };  

d -- 填充ndev->wireless_handlers结构体成员变量,该变量是无线扩展功能

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ndev->wireless_handlers = (struct iw_handler_def *)&xxx_handlers_def;  

e -- 将ndev设备注册到网络子系统

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. register_netdev(ndev);  

3、 WIFI设备本身私有的功能及属性

      如自身的配置及初始化、建立与用户空间的交互接口、自身功能的实现等。

a -- 自身的配置及初始化

代码如下:                

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. xxx_read_chip_info();  
  2.   
  3. xxx_chip_configure();  
  4.   
  5. xxx_hal_init();  

b -- 主要是在proc和sys文件系统上建立与用户空间的交互接口

代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. xxx_drv_proc_init();  
  2.   
  3. xxx_ndev_notifier_register();  

c -- 自身功能的实现

      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模块,首先我们在驱动源码中大致添加以下几步工作(前面分析过,这里只看步骤,不看代码):

a -- 定义一个usb_driver结构体变量

b -- 填充该设备的usb_driver结构体成员变量

c -- 将该驱动注册到USB子系统

      简单完成以上几步工作,再加上板级文件(arch/mach-xxx.c)对USB设备的支持,Linux的USB子系统几乎可以挂载该wifi模块为USB设备了。但是这并不是我们最终想要的结果。我们还要让Linux系统知道它挂载的USB设备属于无线网络设备,同时能够访问它,利用它实施无线网络的工作

     我们都知道,若要让USB设备真正工作起来,需要对USB设备的4个层次(设备、配置、接口、端点)进行初始化。当然这四个层次并不是一定都要进行初始化,而是根据你的USB设备的功能进行选择的,大致初始化流程如下伪代码:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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.     //设备  
  9.     struct usb_device *pusbd;  
  10.     struct usb_device_descriptor *pdev_desc;  
  11.   
  12.     //配置  
  13.     struct usb_host_config *phost_conf;  
  14.     struct usb_config_descriptor *pconf_desc;  
  15.   
  16.     //接口  
  17.     struct usb_host_interface *phost_iface;  
  18.     struct usb_interface_descriptor *piface_desc;  
  19.       
  20.     //端点  
  21.     struct usb_host_endpoint *phost_endp;  
  22.     struct usb_endpoint_descriptor *pendp_desc;  
  23.   
  24.   
  25.     //设备的初始化  
  26.     pdvobjpriv->pusbintf = usb_intf ;  
  27.     pusbd =pdvobjpriv->pusbdev = interface_to_usbdev(usb_intf);  
  28.     usb_set_intfdata(usb_intf, pdvobjpriv);  
  29.     pdev_desc =&pusbd->descriptor;  
  30.   
  31.       
  32.     //配置的初始化  
  33.     phost_conf =pusbd->actconfig;  
  34.     pconf_desc =&phost_conf->desc;  
  35.   
  36.    
  37.     //接口的初始化  
  38.     phost_iface =&usb_intf->altsetting[0];  
  39.     piface_desc =&phost_iface->desc;  
  40.   
  41.       
  42.     //端点的初始化,由于wifi模块属于网络设备,传输批量数据,因此需要初始化为批量端点,端点方向(输入、输出)等。同时,由于wifi驱动功能比较多,需要初始化几个输入输出端点。  
  43.     for (i = 0; i <pdvobjpriv->nr_endpoint; i++)  
  44.     {  
  45.         phost_endp = phost_iface->endpoint +i;  
  46.         if (phost_endp)  
  47.         {  
  48.             pendp_desc =&phost_endp->desc;  
  49.   
  50.             //检查是否为输入端点  
  51.             usb_endpoint_is_bulk_in(pendp_desc);  
  52.   
  53.             //检查是否为输出端点  
  54.             usb_endpoint_is_bulk_out(pendp_desc);  
  55.         }  
  56.   
  57.     }  
  58.   
  59.     usb_get_dev(pusbd);  
  60.   
  61. }  

      完成以上的初始化工作之后,接下来我们需要理清一下USB接口的作用,它是wifi芯片内部的固件程序与主机上的Linux系统进行数据通信。USB设备通信不像普通字符设备那样采用I/O内存和I/O端口的访问,而是采用一种称为URB(USB Request Block)的USB请求块,URB在整个USB子系统中,相当于通电设备中的“电波”,USB主机与设备的通信,通过“电波”来传递。下面我们就来编写USB接口的读写操作函数,伪代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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.     //当需要进行批量数据的读取时,采用以下操作  
  9.     pops->_read_port = &usb_read_port;     
  10.   
  11.     //当需要进行简单数据的写时,采用以下操作  
  12.     pops->_write8 = &usb_write8;  
  13.     pops->_write16 = &usb_write16;  
  14.     pops->_write32 = &usb_write32;  
  15.     pops->_writeN = &usb_writeN;  
  16.   
  17.     //当需要进行批量数据的写时,采用以下操作  
  18.     pops->_write_port = &usb_write_port;  
  19.   
  20.     //取消读写urb  
  21.     pops->_read_port_cancel = &usb_read_port_cancel;  
  22.     pops->_write_port_cancel = &usb_write_port_cancel;  
  23.   
  24. }  

         在进行批量数据的读写时,如usb_read_port()和usb_write_port()函数,需要完成urb创建、初始化、提交、完成处理这个完整的流程。伪代码如下:

1)批量读操作

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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.   
  9.     //创建urb,这里是在其它地方创建完成之后,传递过来  
  10.     purb =precvbuf->purb;  
  11.   
  12.     //初始化批量urb  
  13.     usb_fill_bulk_urb(purb, pusbd, pipe,  
  14.         precvbuf->pbuf,  
  15.         MAX_RECVBUF_SZ,  
  16.         usb_read_port_complete,  
  17.         precvbuf);//contextis precvbuf  
  18.       
  19.     //提交urb  
  20.     err =usb_submit_urb(purb, GFP_ATOMIC);  
  21.   
  22. }  

2)批量写操作

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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.   
  7.     structxmit_priv       *pxmitpriv =&padapter->xmitpriv;  
  8.     structxmit_buf *pxmitbuf = (struct xmit_buf *)wmem;  
  9.     structxmit_frame *pxmitframe = (struct xmit_frame *)pxmitbuf->priv_data;  
  10.     structusb_device *pusbd = pdvobj->pusbdev;  
  11.     structpkt_attrib *pattrib = &pxmitframe->attrib;  
  12.   
  13.     //创建urb,这里是在其它地方创建完成之后,传递过来  
  14.      purb = pxmitbuf->pxmit_urb[0];  
  15.   
  16.     //初始化批量urb  
  17.     usb_fill_bulk_urb(purb, pusbd, pipe,  
  18.         pxmitframe->buf_addr,//= pxmitbuf->pbuf  
  19.         cnt,  
  20.         usb_write_port_complete,  
  21.         pxmitbuf);//contextis pxmitbuf  
  22.   
  23.     //提交urb  
  24.     status = usb_submit_urb(purb,GFP_ATOMIC);  
  25.   
  26.     return ret;  
  27.   
  28. }  

        完成以上批量数据的读写操作之后,大家可能会疑问:这不是一般USB设备驱动的操作流程吗?貌似和wifi没有半毛钱的关系啊!从上面看,确实和wifi没有任何联系,但是以上只是一个铺垫。我们一直强调USB接口在wifi模块中充当什么角色,既然是接口,那么它就是为数据传输而生。所以,和wifi扯上关系的就在于usb_read_port()usb_write_port()这两个函数。


三、读写函数分析

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

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

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

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


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

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

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

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

d -- 将URB提交到USB核心;

e -- 提交成功后,URB的完成函数将被USB核心调用。

        我们知道只有当wifi模块有数据可读时,主控端才能成功地读取数据。那么wifi模块什么时候有数据可读呢?——下面重点来了!wifi模块通过RF端接收到无线网络数据,然后缓存到wifi芯片的RAM中,此时,wifi模块就有数据可读了

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

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

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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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.     
  7.     tasklet_init(&precvpriv->recv_tasklet, (void(*)(unsigned long))rtl8188eu_recv_tasklet, (unsigned long)padapter);    
  8.     
  9.     precvpriv->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); //创建一个中断URB    
  10.     
  11.     precvpriv->int_in_buf = rtw_zmalloc(INTERRUPT_MSG_FORMAT_LEN);    
  12.     //init recv_buf    
  13.     _rtw_init_queue(&precvpriv->free_recv_buf_queue);    
  14.     _rtw_init_queue(&precvpriv->recv_buf_pending_queue);    
  15.     
  16.     precvpriv -> pallocated_recv_buf = rtw_zmalloc(NR_RECVBUFF *sizeof(struct recv_buf) + 4);    
  17.     precvbuf = (struct recv_buf*)precvpriv->precv_buf;    
  18.     
  19.     for(i=0; i < NR_RECVBUFF ; i++)    
  20.     {    
  21.         _rtw_init_listhead(&precvbuf->list);    
  22.         _rtw_spinlock_init(&precvbuf->recvbuf_lock);    
  23.         precvbuf->alloc_sz = MAX_RECVBUF_SZ;    
  24.     
  25.         res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf);    
  26.     
  27.         precvbuf->ref_cnt = 0;    
  28.         precvbuf->adapter =padapter;    
  29.         precvbuf++;    
  30.     }    
  31.     precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF;    
  32.     
  33.     skb_queue_head_init(&precvpriv->rx_skb_queue);    
  34.     
  35. #ifdef CONFIG_PREALLOC_RECV_SKB    
  36.     {    
  37.         int i;    
  38.         SIZE_PTR tmpaddr=0;    
  39.         SIZE_PTR alignment=0;    
  40.         struct sk_buff *pskb=NULL;    
  41.         skb_queue_head_init(&precvpriv->free_recv_skb_queue);    
  42.         for(i=0; i<NR_PREALLOC_RECV_SKB; i++)    
  43.         {    
  44.             pskb = rtw_skb_alloc(MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);    
  45.             if(pskb)    
  46.             {    
  47.                 pskb->dev = padapter->pnetdev;    
  48.                 tmpaddr = (SIZE_PTR)pskb->data;    
  49.                 alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);    
  50.                 skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment));    
  51.                 skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);    
  52.             }    
  53.             pskb=NULL;    
  54.         }    
  55.     }    
  56. #endif    
  57.     return res;    
  58. }    

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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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.     
  7.     precvbuf->irp_pending = _FALSE;    
  8.     precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL); //创建一个批量URB    
  9.     
  10.     precvbuf->pskb = NULL;    
  11.     precvbuf->reuse = _FALSE;    
  12.     precvbuf->pallocated_buf  = precvbuf->pbuf = NULL;    
  13.     precvbuf->pdata = precvbuf->phead = precvbuf->ptail = precvbuf->pend = NULL;    
  14.     precvbuf->transfer_len = 0;    
  15.     precvbuf->len = 0;    
  16.     
  17.     #ifdef CONFIG_USE_USB_BUFFER_ALLOC_RX    
  18.     precvbuf->pallocated_buf = rtw_usb_buffer_alloc(pusbd, (size_t)precvbuf->alloc_sz, &precvbuf->dma_transfer_addr);  //创建一个DMA缓冲区    
  19.     precvbuf->pbuf = precvbuf->pallocated_buf;    
  20.     if(precvbuf->pallocated_buf == NULL)    
  21.         return _FAIL;    
  22.     #endif //CONFIG_USE_USB_BUFFER_ALLOC_RX    
  23.         
  24.     return res;    
  25. }    

      在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()将被调用。伪代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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.     
  10.     rtl8188eu_init_recvbuf(adapter, precvbuf);          
  11.     
  12.     precvpriv->rx_pending_cnt++;    
  13.     
  14.     purb = precvbuf->purb;    
  15.     
  16.     //translate DMA FIFO addr to pipehandle    
  17.     pipe = ffaddr2pipehdl(pdvobj, addr);    
  18.     
  19.     usb_fill_bulk_urb(purb, pusbd, pipe,     
  20.                     precvbuf->pbuf,    
  21.                             MAX_RECVBUF_SZ,    
  22.                             usb_read_port_complete,    
  23.                             precvbuf);//context is precvbuf    
  24.     
  25.     err = usb_submit_urb(purb, GFP_ATOMIC);    
  26.     
  27.     return ret;    
  28. }    
     通过上面的代码,我们可以得知在wifi模块为接收数据做初始化准备时,分配了URB和DMA缓冲区。而在usb_read_port()函数中初始化URB和提交URB。

< 未完待续.....>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401887.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析

SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块&#xff0c;内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈&#xff0c;能够实现用户主平台数据通过SDIO口到无线网络之间的转换。SDIO具有传输数据快&#xff0c;兼容SD、MMC接口等特点。 对于SDIO接口的w…

Erlang并发机制 –进程调度

2019独角兽企业重金招聘Python工程师标准>>> Erlang调度器主要完成对Erlang进程的调度&#xff0c;它是Erlang实现软件实时和进程之间公平使用CPU的关键。Erlang运行时&#xff0c;有4种任务需要被调度&#xff1a;进程&#xff0c;Port&#xff0c;Linked-in drive…

Linux 下wifi 驱动开发(二)—— WiFi模块浅析

一、什么是wifi 模块 百度百科上这样定义&#xff1a; Wi-Fi模块又名串口Wi-Fi模块&#xff0c;属于物联网传输层&#xff0c;功能是将串口或TTL电平转为符合Wi-Fi无线网络通信标准的嵌入式模块&#xff0c;内置无线网络协议IEEE802.11b.g.n协议栈以及TCP/IP协议栈。传统的硬件…

Linux 下wifi 驱动开发(一)—— WiFi基础知识解析

一、WiFi相关基础概念 1、什么是wifi 我们看一下百度百科是如何定义的&#xff1a; Wi-Fi是一种可以将个人电脑、手持设备&#xff08;如pad、手机&#xff09;等终端以无线方式互相连接的技术&#xff0c;事实上它是一个高频无线电信号。[1] 无线保真是一个无线网络通信技术…

Linux 网络设备驱动开发(一) —— linux内核网络分层结构

Linux内核对网络驱动程序使用统一的接口&#xff0c;并且对于网络设备采用面向对象的思想设计。 Linux内核采用分层结构处理网络数据包。分层结构与网络协议的结构匹配&#xff0c;既能简化数据包处理流程&#xff0c;又便于扩展和维护。 一、内核网络结构 在Linux内核中&#…

Linux 网络设备驱动开发(二) —— Linux 网络栈剖析

一、协议简介 虽然对于网络的正式介绍一般都参考了 OSI&#xff08;Open Systems Interconnection&#xff09;模型&#xff0c;但是本文对 Linux 中基本网络栈的介绍分为四层的 Internet 模型&#xff08;如图 1 所示&#xff09;。 图 1. 网络栈的 Internet 模型 这个栈的最底…

Linux 网络设备驱动开发(三) —— 网络设备驱动基本原理和框架

一、协议栈层次对比 二、Linux网络子系统 Linux网络子系统的顶部是系统调用接口层。它为用户空间提供的应用程序提供了一种访问内核网络子系统的方法&#xff08;socket&#xff09;。位于其下面是一个协议无关层&#xff0c;它提供一种通用的方法来使用传输层协议。然后是具…

国产杀毒软件也开始支持虚拟化

今天不小心看到一个较老的“新”闻&#xff0c;国产安全软件厂商瑞星已经推出支持VMware vshield Endpoint技术的防病毒产品&#xff0c;也就是说如果客户今天使用的是VMware的虚拟化软件&#xff0c;上面运行的所有虚拟机就不需要安装传统的防病毒软件程序&#xff0c;而可以直…

VRP平台基本操作

一、显示系统信息 <Huawei>display version 图上所示可以知道VRP平台信息&#xff0c;运行的版本&#xff0c;运行的时间 二、修改和查看设备系统时间参数 1.查看时间 <Huawei>display clock 2.修改系统日期和时间 三、进入系统视图界面 <Huawei>system-view…

Android中获取应用程序(包)的信息-----PackageManager的使用(一)

本节内容是如何获取Android系统中应用程序的信息&#xff0c;主要包括packagename、label、icon、占用大小等。具体分为两个 部分&#xff0c;计划如下&#xff1a; 第一部分&#xff1a; 获取应用程序的packagename、label、icon等 &#xff1b; 第二部分&#xff1a; 获取应用…

VRP平台总体介绍及基础配置

前言 1、VRP软件系统基础 VRP系统在启动时需要加载“系统软件”和“配置文件”两部分&#xff0c;这与其它品牌网络交换机的操作系统是一样的。如果指定了下次启动的补丁文件&#xff0c;还需加载补丁文件。修改VRP系统启动的场景一般有以下几种&#xff1a; a-- 对交换机进行升…

小强的HTML5移动开发之路(43)——JqueryMobile页眉、工具栏和标签栏导航

一、页眉1、添加页眉和页脚<div data-role"header"><h1>第 1 页</h1></div><div data-role"footer"><h4>页面脚注</h4></div>默认的页眉在屏幕的顶部边缘显示&#xff0c;而且在在屏幕滚动时&#xff0c;…

交换机开发(二)—— 三层交换机报文转发过程

如图所示&#xff0c;假如主机A想访问主机B&#xff0c;首先主机A会将自己的IP地址和子网掩码做与操作,得出网路地址(如:Host-A的IP地址100.1.1.2与自身掩码255.255.255.0做与操作后,得到的网络号是100.1.1.0).然后判断目的IP地址(即Host-B的IP地址)与自己的网络地址是不是在同…

分布式搜索elasticsearch配置文件详解

2019独角兽企业重金招聘Python工程师标准>>> elasticsearch的config文件夹里面有两个配置文件&#xff1a;elasticsearch.yml和logging.yml&#xff0c;第一个是es的基本配置文件&#xff0c;第二个是日志配置文件&#xff0c;es也是使用log4j来记录日志的&#xff…

交换机开发(三)—— 深入分析三层网络交换机的原理和设计

引言传统路由器在网络中起到隔离网络、隔离广播、路由转发以及防火墙的作业&#xff0c;并且随着网络的不断发展&#xff0c;路由器的负荷也在迅速增长。其中一个重要原因是出于安全和管理方便等方面的考虑&#xff0c;VLAN(虚拟局域网)技术在网络中大量应用。VLAN技术可以逻辑…

XML 命名空间(XML Namespaces)

为什么80%的码农都做不了架构师&#xff1f;>>> XML 应用程序 XML CDATA XML 命名空间提供避免元素命名冲突的方法。 命名冲突 在 XML 中&#xff0c;元素名称是由开发者定义的&#xff0c;当两个不同的文档使用相同的元素名时&#xff0c;就会发生命名冲突。 这个…

Linux 下挂载新硬盘方法

Linux的硬盘识别: 一般使用”fdisk -l”命令可以列出系统中当前连接的硬盘 设备和分区信息.新硬盘没有分区信息,则只显示硬盘大小信息. 1.关闭服务器加上新硬盘 2.启动服务器&#xff0c;以root用户登录 3.查看硬盘信息 #fdisk -l [cpp] view plaincopy Disk /dev/sda: 42.9 GB…

C++ 学习基础篇(一)—— C++与C 的区别

编程的学习学无止境&#xff0c;只掌握一门语言是远远不够的&#xff0c;现在我们开始C的学习之路&#xff0c;下面先看下C 与C 的区别 一、C概述 1、发展历史 1980年&#xff0c;Bjarne Stroustrup博士开始着手创建一种模拟语言&#xff0c;能够具有面向对象的程序设计特色。在…

C++学习基础篇 —— 引用()的用法和应用

一、引用简介 引用就是某一变量&#xff08;目标&#xff09;的一个别名&#xff0c;对引用的操作与对变量直接操作完全一样。 引用的声明方法&#xff1a;类型标识符&引用名目标变量名&#xff1b; 【例1】&#xff1a; [cpp] view plaincopy int a; int &raa; //定义…

C++基础知识(二)—— 变量和数据类型

你可能觉得这个“Hellow World”程序用处不大。我们写了好几行代码&#xff0c;编译&#xff0c;然后执行生成的程序只是为了在屏幕上看到一句话。的确&#xff0c;我们直接在屏幕上打出这句话会更快。但是编程并不仅限于在屏幕上打出文字这么简单的工作。为了能够进一步写出可…