USB WiFi网卡驱动分析--经典

初始化:

在Linux下,驱动大多都是module,可以被自由的装载卸载。Rt73 usb wireless驱动也不例外,是一个可独立编译的module。Module一般都是由module_init入口,module_exit出口。

Init所做的工作就是将rtusb驱动挂入到内核usb驱动链中:returnusb_register(&rtusb_driver);

 

rtusb_driver的定义如下:

struct usb_driver rtusb_driver = {

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)

   .owner = THIS_MODULE,

#endif

   .name="rt73",

   .probe=usb_rtusb_probe,

   .disconnect=usb_rtusb_disconnect,

   .id_table=rtusb_usb_id,

#ifdef CONFIG_PM

   .suspend = rt73_suspend,

   .resume = rt73_resume,

#endif

 };

 

usb_rtusb_probe函数是最重要的,主要用于设备的探测以及所需内核资源的初始化。rt73_suspend和rt73_resume主要是电源管理相关,如收到suspend时以怎样的低功耗工作,以及resume时如何恢复等。

usb_rtusb_disconnect是当usb设备从系统中移除之后会调用接口。rtusb_usb_id是驱动支持的usb设备ID列表:

struct usb_device_id   rtusb_usb_id[] = RT73_USB_DEVICES;

#define RT73_USB_DEVICES { \

    {USB_DEVICE(0x148f,0x2573)}, /* Ralink*/      \

    {USB_DEVICE(0x148f,0x2671)}, /* Ralink*/      \

{ }/* Terminating entry */       \

} /* end marker */

Module初始化时向系统注册完之后,驱动就会扫描系统总线,查找是否有此驱动相应的设备在总线上,如果有设备就会调用probe函数;

或者驱动初始化后,再把设备插到系统上,此时也会从总线的中断触发到驱动的probe函数。两种调用的触发方式不同。

 

Probe函数:

内核程序中Usb设备驱动有一个骨架程序,描述了一个最基本的usb驱动应该具备的方法以及调用的最简单接口。因此本部分涉及基本调用不再赘述,参照骨架程序理解。

因为rt73 usb wireless为usb设备的同时,还是网络设备,需要用到网络设备驱动程序的基本结构和方法。

struct net_device   *netdev;

netdev = alloc_etherdev(sizeof(PRTMP_ADAPTER));

PRTMP_ADAPTER是最核心的网络设备驱动结构,包含了所有网络设备驱动层需要的成员(是否恰当?),再对netdev的成员进行赋值,

netif_stop_queue(netdev); //设置链路状态是off

ether_setup(pAd->net_dev); //以太网接口的网络接口设置函数

    netdev->open = usb_rtusb_open;

    netdev->stop = usb_rtusb_close;

    netdev->hard_start_xmit =usb_rtusb_sendpkt;

    netdev->get_stats = rt73_get_ether_stats;

    netdev->do_ioctl = rt73_ioctl;

netdev->wireless_handlers = (struct iw_handler_def *)&rt73_iw_handler_def;

rt73_iw_handler_def定义了用于wireless的一部分接口。

网络设备初始化完成之后,向内核中注册:

res = register_netdev(pAd->net_dev);

usb_set_intfdata(intf, pAd);

然后会初始化usb device:

 if(usb_rtusb_init_device(pAd->net_dev)!=NDIS_STATUS_SUCCESS)//很重要

 

在初始化函数usb_rtusb_init_device中主要完成数据接收的初始化处理,使用tasklet,处理函数是RTUSBBulkRxHandle,

tasklet_init(&pAd->rx_bh, RTUSBBulkRxHandle, (unsignedlong)pAd);

函数RTUSBBulkRxHandle会调用RTUSBRxPacket。

初始化完发送和接收需要的相关结构后,初始化mlme相关资源。主要是mlme队列,以及不同的处理函数,调用CreateThreads进行MlmeThread(网络管理队列线程)RTUSBCmdThreadusb命令队列线程两个内核线程的创建。

Mlme线程是用于设置连接路由器的工作,相当于是一个状态机,如状态ASSOC_STATE_MACHINE等,设置wep,wpa密码等连接方式主要由这个线程完成,主要是基于PRTMP_ADAPTER的mlme结构体的mlme_Queue完成业务处理;

RTUSBCmdThread主要负责是一个转换线程,将对网络接口的相关设置进行转换到mlme中,包括AP搜索,数据传输,设置网络接口参数ioctl等,它主要是基于PRTMP_ADAPTER的cmdQ进行业务逻辑处理。

 

MLME线程:

   此线程的核心结构是MLME_STRUCT,

typedef struct _MLME_STRUCT {

 STATE_MACHINE  CntlMachine;   //不同状态机状态—控制

 STATE_MACHINE  AssocMachine;//关联

 STATE_MACHINE  AuthMachine;//认证

 STATE_MACHINE  AuthRspMachine;//认证回应

 STATE_MACHINE  SyncMachine;//同步

 STATE_MACHINE  WpaPskMachine;//wpapsk

 STATE_MACHINE_FUNC  AssocFunc[ASSOC_FUNC_SIZE];

 STATE_MACHINE_FUNC  AuthFunc[AUTH_FUNC_SIZE];

 STATE_MACHINE_FUNC  AuthRspFunc[AUTH_RSP_FUNC_SIZE];

 STATE_MACHINE_FUNC  SyncFunc[SYNC_FUNC_SIZE];

 STATE_MACHINE_FUNC  WpaPskFunc[WPA_PSK_FUNC_SIZE];

 ULONG      ChannelQuality;  // 0..100,Channel Quality Indication for Roaming

 ULONG      Now32;    // latch the value of

 NdisGetSystemUpTime() 

 BOOLEAN    bRunning;

 spinlock_t   TaskLock;

 MLME_QUEUE   Queue;

 UINT      ShiftReg;

 RALINK_TIMER_STRUCT  PeriodicTimer;

 RALINK_TIMER_STRUCT  LinkDownTimer;

 ULONG      PeriodicRound;

 MLME_MEMORY_HANDLER  MemHandler;  //The handler of the nonpaged memory inside MLME  

} MLME_STRUCT, *PMLME_STRUCT;

每种状态对应相应的状态处理函数,即STATE_MACHINE_FUNC。相关成员的初始化赋值是在MlmeInit中完成的。每种状态中又细分了多个阶段,每个阶段都有对应的不同处理。如AssocStateMachineInit中体现的。每个阶段是用结构体MLME_QUEUE_ELEM记录的。

typedef struct _MLME_QUEUE_ELEM {

   UCHAR             Msg[MAX_LEN_OF_MLME_BUFFER];  // add by johnli, fix the bug of alignment

   ULONG             Machine;

   ULONG             MsgType;

   ULONG             MsgLen;

   LARGE_INTEGER     TimeStamp;

   UCHAR             Rssi;

   UCHAR             Signal;

   UCHAR            Channel;

   BOOLEAN          Occupied;

   BOOLEAN          bReqIsFromNdis;

//UCHAR Msg[MAX_LEN_OF_MLME_BUFFER]; //remove by johnli, move above, fix the bug of alignment

} MLME_QUEUE_ELEM, *PMLME_QUEUE_ELEM;

多个MLEM_QUEUE_ELEM组成了MLME_QUEUE,然后由MLME线程统一处理。MLME_QUEUE是PRTMP_ADAPTER的一个成员。

typedef struct _MLME_QUEUE {

   ULONG             Num;

   ULONG             Head;

   ULONG             Tail;

   spinlock_t        Lock;

   MLME_QUEUE_ELEM  Entry[MAX_LEN_OF_MLME_QUEUE];

} MLME_QUEUE,*PMLME_QUEUE;

 

MLME处理的几种状态有:

ASSOC_STATE_MACHINE           关联状态 

AUTH_STATE_MACHINE            认证状态

AUTH_RSP_STATE_MACHINE        认证回应状态

SYNC_STATE_MACHINE              同步状态

MLME_CNTL_STATE_MACHINE         控制状态

WPA_PSK_STATE_MACHINE           WPAPSK状态

 

MLME状态机,代码比较复杂,暂不分析。Mlme部分相关的几个源文件:assoc.cauth.c auth_rsp.c connect.c mlme.c mlme_ex.c sync.c

 

CMDHandler线程提供---服务。将从网络设备来的上层服务,转化为MLME并加入其对列处理中,最终传递到usb层,或者直接调用usb的相关服务函数。从本质上讲是一个服务转化层。

数据是如何发送的:

初始化发送和接收相关的数据结构:

Status = NICInitTransmit(pAd);      //if fail clean itself

Status = NICInitRecv(pAd);

Usb层发送数据处理函数:当网络层有数据需要发送时,就会调用usb_rtusb_sendpkt函数

netdev->hard_start_xmit =usb_rtusb_sendpkt;,此函数又会调用RTMPSendPackets函数。

dev->hard_start_xmit 是一个回调函数,值设置为dsr_dev_start_xmit。这个函数不是驱动程来调用的,而是用由上层协议来调用的。如IP或ARP层需要发送报文时,它会根据目标IP地址来决定通过哪个网络设备net_dev来传输数据,然后调用它的hard_start_xmit回调函数。

IP/TCP协议栈的调用和控制在内核已实现好了,只有数据来到设备时,才调用驱动的回调函数。这也是驱动程序实现的原理,网络程动与其它程驱动就一样的。

    对rt73 usb驱动而言,真正的数据发送是通过usb完成的,在RTMPSendPackets函数中又会调用RTUSBKickBulkOut,完成数据的发送。发送函数中会发送的数据类型包括以下几类:

  //1. Data Fragment has highest priority

RTUSBBulkOutDataPacket

  //2. PS-Poll frame is next

RTUSBBulkOutPsPoll

  //5. Mlme frame is next

RTUSBBulkOutMLMEPacket

  //6. Data frame normal is next

RTUSBBulkOutDataPacket

  //7. Null frame is the last

RTUSBBulkOutDataPacket

  //8. No data available

RTUSBBulkOutNullFrame

数据发送是放在不同的环形buffer中的。发送的最后环节是rtusb_submit_urb,将数据放到申请的urb中,提交给usb core处理。处理完毕之后,调用rt73 网络驱动注册的回调函数RTUSBBulkOutDataPacketComplete。(此处一直怀疑是导致内存OOM的root cause)。

 

数据的接收处理过程:

VOID RTUSBBulkRxHandle( IN unsigned longdata)

{

 purbb_t  pUrb = (purbb_t)data;

 PRTMP_ADAPTER  pAd;

 PRX_CONTEXT  pRxContext;

 pRxContext = (PRX_CONTEXT)pUrb->context;

 pAd= pRxContext->pAd;

 /*device had been closed */

 if(RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_REMOVE_IN_PROGRESS)) 

 return;

 if(pUrb->status != 0)

 {

  RTUSBBulkReceive(pAd);

 return;

 }

 RTUSBRxPacket(data);

 return;

}

/*

 ========================================================================

 Routine Description:

 USB_RxPacket initializes a URB and uses the RxIRP to submit it

 toUSB. It checks if an Rx Descriptor is available and passes the

 thecoresponding buffer to be filled. If no descriptor is available

 fails the request. When setting the completionroutine we pass our

 Adapter Object as Context.

 Return Value:

   TRUE   found matched tuple cache

  FALSE   no matched found

 Note:

 ========================================================================

*/

VOID RTUSBBulkReceive(

/*

 ========================================================================

  Description:

 This is the completion routine for the USB_RxPacket which submits

  aURB to USBD for a transmission. 

 ========================================================================

*/

VOID RTUSBRxPacket(

RTUSBRxPacket函数中有802.11协议栈处理。Usb部分收到数据之后,调用网卡驱动的接收函数,进行简单的协议栈层处理,并将数据拷贝的之上的网络层(sk_buf)。

Ioctl处理:

netif_rx

rtusb_io.c----usb驱动的IOTCTL处理

ioctl列表:

RT_OID_CHECK_GPIO:读gpio寄存器信息

OID_802_11_BSSID_LIST_SCAN:搜索AP

RT_OID_PERIODIC_EXECUT:

RT_OID_802_11_BSSID:

OID_802_11_SSID:

OID_802_11_DISASSOCIATE:分离关联

 

netif_block.c---接口调用net_devices.c中提供的接口

/*

 *INET  An implementation of the TCP/IPprotocol suite for the LINUX

 * operating system.  INET isimplemented using the  BSD Socket

 * interface as the means of communication with the user level.

 * Definitions for the Interfaces handler.

/**

 *netif_wake_queue - restart transmit

 *@dev: network device

 *Allow upper layers to call the device hard_start_xmit routine.

 *Used for flow control when transmit resources are available.

 */

/**

 *netif_stop_queue - stop transmitted packets

 *@dev: network device

 *Stop upper layers calling the device hard_start_xmit routine.

 *Used for flow control when transmit resources are unavailable.

 */

驱动已经准备完毕,应用程序需要什么服务?应用程序要使用wifi网卡进行数据传输,首先需要进行设置。完毕之后,会创建socket进行数据传输。

wifi进行 AP搜索时,会短时间内中断数据传输。

问题原因:AP搜索时会导致“短暂”的数据传输中断,驱动里有相关注释

case CNTL_WAIT_OID_LIST_SCAN:

if(Elem->MsgType == MT2_SCAN_CONF) 

{

//resume txring after scanning complete,we hope the out-of-service time will not be too long to let upperlayer

//time-out the waiting frames

RTUSBResumeMsduTransmission(pAd);

Rt73 ioctl

break;

case RT_PRIV_IOCTL:

subcmd=wrq->u.data.flags;

if(subcmd&OID_GET_SET_TOGGLE) Status =RTMPSetInformation(pAd, rq, subcmd);

else Status = RTMPQueryInformation(pAd, rq,subcmd);

break;

struct iwreq *wrq=(struct iwreq *)rq;

//使用时填充好iwreq的各个变量,wrq->u.data.flags=RT_OID_802_11_EXTRA_INFO,wrq->u.data.pointer会是状态信息。

case OID_GEN_MEDIA_CONNECT_STATUS:  

case RT_OID_802_11_EXTRA_INFO:

  wrq->u.data.length = sizeof(ULONG);

  Status = copy_to_user(wrq->u.data.pointer,&pAdapter->ExtraInfo, wrq->u.data.length);

  DBGPRINT(RT_DEBUG_TRACE, "Query::RT_OID_802_11_EXTRA_INFO(=%d)\n", pAdapter->ExtraInfo);

  break;

}

你可能感兴趣的:(USB WiFi网卡驱动分析--经典)