wince下DM9000A网卡驱动移植及学习总结---2
2013-07-13 23:52
309 查看
wince下DM9000A网卡驱动移植及学习总结---2
下面我将详细分析整个网卡驱动。1.
Driver.cpp中有函数入口:DriverEntry,初始化一个Miniport Driver时该函数会被第一个调用,用来注册一个Miniport Driver,该函数原型如下:
NDIS_STATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
参数说明:
DriverObject:指向一个由系统创建的驱动对象
PUNICODE_STRING:指向注册表中该驱动参数的路径----注册表路径
该函数中首先调用NdisMInitializeWrapper函数来通知NDIS Library要注册一个Miniport。(可以参考MSDN说明)然后会初始化NDIS40_MINIPORT_CHARACTERISTICS结构体,所有的Miniport的相关接口函数(形如MiniportXXXX的函数)都会赋值到NDIS40_MINIPORT_CHARACTERISTICS结构中,最后调用NdisMRegisterMiniport来注册Miniport。
2.
下面将进入初始化函数:MiniportInitialize,函数原型如下:NDIS_STATUS MiniportInitialize( OUT PNDIS_STATUS OpenErrorStatus,// 额外的状态信息,一般不会被使用 OUT PUINT SelectedMediaIndex, //被选中的媒介类型的索引号,以太网一般是 //NdisMedium802_3 IN PNDIS_MEDIUM MediaArray, //媒介类型数组,包含了不同类型的网络媒介 IN UINT MediaArraySize,// 媒介类型数组大小 IN NDIS_HANDLE MiniportHandle, //适配器句柄,该参数要被保存,以后调用Ndisxxx函数 //时会被用到。 IN NDIS_HANDLE WrapperConfigHandle)// 一个封装配置句柄,会被 //NdisOpenConfiguration函数用到。
此函数主要完成初始化网卡的功能。首先,NIC_DRIVER_OBJECT类(Driver类)实例化如下:
NIC_DRIVER_OBJECT *pnic; if(!(pnic = new NIC_DRIVER_OBJECT(MiniportHandle,WrapperConfigHandle))) //用到了上述传入的两个句柄 return NDIS_STATUS_FAILURE;
实例化后调用其类成员函数:pnic->EDriverInitialize,下面我们看此函数(driver.cpp文件中)。
首先看到语句:
if(!m_pLower){ m_pLower = DeviceEntry(this,NULL);}
m_pLower是Driver.h中:
NIC_DEVICE_OBJECT *m_pLower;
定义的,此时m_pLower等于NULL,所以会调用函数:DeviceEntry,此函数是设备类的入口,在Device.cpp中,打开发现只有一条语句:
return new C_DM9000(pDriverObject,pVoid);
所以此时相当于给Driver类的成员变量m_pLower(Device类)实例化。我们继续看函数EDriverInitialize,然后几条语句如下:
// Determinate media type for(i=0; i<MediaArraySize; i++) if(MediaArray[i] == NdisMedium802_3) break; if (i == MediaArraySize) { THROW((ERR_STRING("Unsupported media"),DIS_STATUS_UNSUPPORTED_MEDIA));} *SelectedMediaIndex = i;
是判断media type,上面说过以太网一般是NdisMedium802_3,从输入的MediaArray数组中得到SelectedMediaIndex(被选中的媒介类型的索引号)。
继续看函数EDriverInitialize,接下来:
// Read registry configurations NdisOpenConfiguration( &status, &hconfig, m_NdisWrapper);
调用函数NdisOpenConfiguration,查看MSDN发现,这个函数是用来返回注册表键值,保存在hconfig(注册表键值路径信息)中,后面会用到。
继续看函数EDriverInitialize,接下来:
m_pLower->DeviceSetDefaultSettings(); m_pLower->DeviceSetEepromFormat(); m_pLower->DeviceRetriveConfigurations(hconfig); m_pLower->EDeviceValidateConfigurations();
调用m_pLower(Device类)的成员函数,主要完成Device类成员数组m_szConfigures[]、m_szEepromFormat[]和m_szCurrentSettings[]的配置。
DeviceSetDefaultSettings不是虚函数,主要完成Device类成员数组:m_szConfigures[]和m_szCurrentSettings[]的默认配置;
DeviceSetEepromFormat是虚函数,真正实现是在Dm9isa.cpp中,(其实不论在Device.cpp还是Dm9isa.cpp,由于DeviceDm9000是Dm9000的父类,所以效果都是一样,相当于是Device,后面不再区分),也是对Device成员数组:m_szEepromFormat[]的初始化设置;
DeviceRetriveConfigurations(hconfig),这个函数完成的功能是继续设置成员数组m_szConfigures[]的值:从前面返回的注册表路径hConfig中读取参数,注册表中没有设置的则使用全局变量g_szDm9ConfigParams的配置默认值。(后面玫瑰红颜色的是DeviceRetriveConfigurations(hconfig)函数详细分析:)
EDeviceValidateConfigurations虚函数,完成的功能:使上述的一些配置使能,即将有效的配置参数写到m_szCurrentSettings[]和m_szConfigures[]数组中。(后面淡蓝颜色的是EDeviceValidateConfigurations函数的详细分析)
继续看函数EDriverInitialize,接下来:
NdisCloseConfiguration(hconfig);//释放句柄 m_pLower->DeviceRegisterAdapter();
释放句柄和前面的NdisOpenConfiguration对应,DeviceRegisterAdapter里面调用了函数NdisMSetAttributesEx,它通知了NDIS库NIC初始化中一些有意义的信息(查MSDN,应该与具体驱动没有太多关系,是NDIS库自己需要处理的东西)。
继续看函数EDriverInitialize,接下来:
/* init tx buffers */ U32 m,uaddr; if(!(uaddr = (U32) malloc(sizeof(DATA_BLOCK) * //data_block由header + 内容buffer的大小 (m=m_pLower->m_szConfigures[CID_TXBUFFER_NUMBER]*2)))) //m=32(0x20)*2=64 THROW((ERR_STRING("Insufficient memory"))); for(;m--;uaddr+=sizeof(DATA_BLOCK)) m_TQueue.Enqueue((PCQUEUE_GEN_HEADER)uaddr);
以上是用malloc函数分配tx
buffer 的大小:64*sizeof(DATA_BLOCK)大,注意:
m_szConfigures[CID_TXBUFFER_NUMBER]来自注册表,我们这里是0x20,Data_Block是有header和buffer组成。有关DATA_BLOCK的详细说明见后面灰色-25%颜色的说明。
分配完大小后,调成员函数m_TQueue (common.h中类:CQueue的实例)进行对tx的data_Block的header进行先后顺序的关系。
继续看函数EDriverInitialize,接下来:
m_pLower->EDeviceRegisterIoSpace(); m_pLower->EDeviceLoadEeprom(); m_pLower->EDeviceInitialize(m_pLower->m_nResetCounts=0); m_pLower->EDeviceRegisterInterrupt();
EDeviceRegisterIoSpace是DM9000A寄存器IO空间的访问,注意:这个函数在Device的父类和子类都实现了,所以会调用子类的函数,进入发现子类函数先调了父类的这个函数再执行其他操作,所以我们还是需要看看父类这个函数的作用,父类其实将先前注册表读得的IOAddress的值转成IO口地址(调用函数MmMapIoSpace),这样与处理器就没有关系,实际上就是我们器件外部的地址总线,这里IOAddress的值为:0x10000000,转换后IO口的地址值为:0x2b0000,这个值就是后面2440访问DM9000的基地址,存于:m_szCurrentSettings[SID_PORT_BASE_ADDRESS]中;子类的函数完成读取DM9000的ID号等操作。这些就比较简单易于理解不再详细介绍。
EDeviceLoadEeprom()虽然在Device类中是个虚函数,但是子类没有实现,父类中有定义,所以调用的是父类中的函数。这个函数其实真正完成的功能是:
m_szEeprom[0]=0xc000;
m_szEeprom[1]=0x2826;
m_szEeprom[2]=0x3096;
EDeviceInitialize(m_pLower->m_nResetCounts=0),这个函数所带的参数是:复位的次数。子类中实现。这个函数实现的真正功能是配置DM9000A一些寄存器,注意完成这个函数后DM9000A并没有打开,中断等都没有使能s(有关DM9000A的初始化EDeviceInitialize,接收数据,发送数据等具体后面再介绍)。
EDeviceRegisterInterrupt()在父类中,主要是调用函数:NdisMRegisterInterrupt,查MSDN发现如下:This
function sets up a mapping between an NIC driver's MiniportISR and MiniportHandleInterrupt functions, already registered with NdisMRegisterMiniport, and the bus-relative vector and level on which its NIC interrupts.即:这个函数设置驱动中MiniportISR和MiniportHandleInterrupt的映射。在我们这里就是driver.cpp中函数DriverIsr和DriverInterruptHandler的映射关系(有关中断后面详细再分析)。
继续看函数EDriverInitialize,接下来:
m_pLower->DeviceOnSetupFilter(0);
DeviceOnSetupFilter(0),子类中实现。带入参数是:0,(这个函数第一次进入,所以完成的功能是将MAC地址写到DM9000A的相应寄存器中)所以执行如下语句:
if(!(m_szCurrentSettings[SID_GEN_CURRENT_PACKET_FILTER]=uFilter)) { /* 1. set unicast */ // retrive node address DeviceMacAddress(&sz[0]); // set node address for(n=0;n<ETH_ADDRESS_LENGTH;n++) { //RETAILMSG(1, (TEXT("DM9_PAR = %x \r\n"), sz )); DeviceWritePort(DM9_PAR0+n,(U32)sz );} /* 2. clear multicast list and count */ m_nMulticasts = 0; memset((void*)&m_szMulticastList,0,sizeof(m_szMulticastList)); /* 3. clear hash table */ // clear hash table memset((void*)(&sz[0]),0,sizeof(sz)); for(n=0;n<sizeof(sz);n++) DeviceWritePort(DM9_MAR0+n,(U32)sz ); return uFilter; }
首先调用DeviceMacAddress函数从m_szEepromFormat[EID_MAC_ADDRESS]中得到MAC地址(注意:要设置初始MAC地址可以在注册表中加入MAC地址参数,在上面读注册表是读入然后赋给m_szEepromFormat[EID_MAC_ADDRESS],在此时则可以读入到DM9000A内部),然后将ETH_ADDRESS_LENGTH(==6)个MAC地址写入DM9000A的物理地址寄存器(10h~15h),接下来,将m_szMulticastList[64][6]全部设置为:0,最后给DM9000A的Multicast
Address Register(16h~1dh)全部设置为0,最后返回uFilter(输入参数,此时为0)。
到此为止函数EDriverInitialize结束。
我们继续看函数MiniportInitialize,上面完成了设备的初始化操作,下面进行流控、中断等设置后就可以打开设备了。发现如下语句:
pnic->DriverStart();
这句就是在Driver.cpp中找到函数:DriverStart如下:
void NIC_DRIVER_OBJECT::DriverStart(void) { m_pLower->DeviceStart(); }
所以调用的是Device类的DeviceStart()函数,此函数子类实现。下面我们详细看看C_DM9000::DeviceStart函数。
首先执行的代码如下:
// set PHY supports flow control DeviceWritePhy(0, 4, (DeviceReadPhy(0,4)|(1<<10)));
这两句是与DM9000A内部PHY相关的,完成的功能是将PHY寄存器4的bit
10 设置为高电平,即使能流控。DeviceReadPhy(0,4)是读PHY寄存器(地址为:4),这里第一个参数0好像没有作用,同样DeviceWrigePhy是写PHY寄存器4。而PHY寄存器4是Auto-negotiation
Advertisement Register (ANAR),bit 10是使能流控与否。这里PHY寄存器读过程需要注意:
1) 往DM9_EPADDR寄存器写入要读的PHY寄存器地址,注意这个寄存器的bit
7:bit6恒等于01,后面bit5:0才是PHY寄存器地址。
2) 往DM9_EPCNTL寄存器写入读PHY的命令。
3) 判断DM9_EPCNTL寄存器bit
0是否等于0,意思为:等待DM9000A自己内部完成读的操作(自己完成后会将要读的数据放置在DM9_EPLOW和DM9_EPHIGH寄存器上)。
4) 往DM9_EPCNTL寄存器写入0,清楚读操作命令。
5) 从DM9_EPLOW和DM9_EPHIGH寄存器读PHY要读取的寄存器的数据。
PHY寄存器写操作类似,不再赘述,详细加源代码。
继续看,代码如下:
val = DeviceReadPort(DM9_NCR); if( val & MAKE_MASK(3)) { /* full duplex mode */ val = DeviceReadPort(DM9_PAUSETH); DeviceWritePort(DM9_PAUSETH,(U8)val); // enable flow control<0> // enable pause packet<5> DeviceWritePort(DM9_FLOW,MAKE_MASK2(5,0)); } else { /* non full duplex mode */ val = DeviceReadPort(DM9_BACKTH); DeviceWritePort(DM9_BACKTH,(U8)val); // enable back pressure<half dumplex mode)<4,3> DeviceWritePort(DM9_FLOW,MAKE_MASK2(4,3)); }
这里是读DM9_NCR(网络控制寄存器)的值(这个reg的bit
3在DM9000A打开内部PHY后是只读的,表示全双工还是半双工),然后判断双工模式进行不同配置。我们这里是全双工模式,主要配置DM9_FLOW(0Ah)的bit
5和bit 0为1:
Bit 5:TXPEN:强制发送暂停包使能。按溢出门限最高值使能发送暂停包。
Bit 1:RXPCS:接收暂停包当前状态。
(这句之前的两句将DM9_PAUSETH值读出有赋回去,不知道有什么作用…)
继续看,代码如下:
// enable interrupt DeviceEnableInterrupt();
使能收发数据包中断等,配置DM9_IMR寄存器的Bit0,1,2,3,7,意思如下:
7:PAR:1使能指针自动跳回。当SRAM的读、写指针超过SRAM的大小时,指针自动跳回起始位置。需要驱动程序设置该位,若设置则REG_F5(MDRAH)将自动位0CH。
3:ROOI:1使能接收溢出计数器溢出中断。
2:ROI:1使能接收溢出中断。
1:PTI:1使能数据包传输中断。
0:PRI:1使能数据包接收中断。
继续看,代码如下:
DeviceWritePort(DM9_RXCR,m_szCurrentSettings[SID_OP_MODE]);
DM9_RXCR接收控制寄存器05h(RX
Control Register )各bit意义如下:
7:保留。
6:WTDIS:看门狗定时器禁止。1禁止,0使能。
5:DIS_LONG:丢弃长数据包。1为丢弃数据包长度超过1522字节的数据包。
4:DIS_CRC:丢弃CRC校验错误的数据包。
3:ALL:忽略所有多点传送。
2:RUNT:忽略不完整的数据包。
1:PRMSC:混杂模式(Promiscuous
Mode)
0:RXEN:接收使能。
这里m_szCurrentSettings[SID_OP_MODE]的值是在初始化函数EDeviceValidateConfigurations中设置的,bit
0、1、4、5为:1,所以条语句完成的功能是室使能接受,即丢弃CRC校验错误的包即长度超过1522字节的包。
到此位置,C_DM9000::DeviceStart函数结束,MiniportInitialize函数也结束,所有的初始化介绍。总的来说,初始化就是设置先DM9000A的工作模式相关的一些变量值,然后用这些值配置DM9000A的寄存器来设置正确的工作模式,最后调用DeviceStart函数,使能流控中断以及打开接受使能等操作。
MiniportInitialize中涉及的相关函数即结构体的分析:
以下是前面提到的两个函数的分析:
void NIC_DEVICE_OBJECT::DeviceRetriveConfigurations( NDIS_HANDLE hConfig) { NDIS_STATUS status; PCONFIG_PARAMETER pconfig; PNDIS_CONFIGURATION_PARAMETER param; for(pconfig=DeviceConfigureParameters(); (pconfig->uId != (U32)-1); pconfig++) { NdisReadConfiguration(//读注册表 &status, ¶m,//读出的参数 hConfig,//前面返回的注册表路径 &(pconfig->szName), NdisParameterHexInteger);//读的类型 if(status == NDIS_STATUS_SUCCESS)//读成功 m_szConfigures[pconfig->uId] = param->ParameterData.IntegerData; else//读失败,证明注册表没有这个参数,使用默认的值 m_szConfigures[pconfig->uId] = pconfig->uDefValue; } }
上面加粗的是个函数,所以得先弄清楚这个函数干了什么,寻找发现这个函数的实现是在Device的子类中,其实这个函数就是从一个全局变量读取配置参数,如下:
PCONFIG_PARAMETER C_DM9000::DeviceConfigureParameters(void) { return (PCONFIG_PARAMETER)&g_szDm9ConfigParams[0]; }
很简单,就是返回了一个全局变量的地址,这个全局变量也在Dm9isa.cpp中,如下:
CONFIG_PARAMETER g_szDm9ConfigParams[] = { { CID_CONNECTION_TYPE, -1, NDIS_STRING_CONST("ConnectionType") }, { CID_SLOT_NUMBER, -1, NDIS_STRING_CONST("SlotNumber")}, {CID_BUFFER_PHYSICAL_ADDRESS,0,NDIS_STRING_CONST("BufferPhysicalAddress")}, { CID_TXBUFFER_NUMBER, 0x20, NDIS_STRING_CONST("XmitBuffer")}, { CID_RXBUFFER_NUMBER, 0x10, NDIS_STRING_CONST("RecvBuffer")}, { CID_ADAPTER_NUMBER, 0, NDIS_STRING_CONST("AdapterNumber")}, { CID_IO_BASE_ADDRESS, 0x300, NDIS_STRING_CONST("IoAddress")}, { CID_IO_RANGE, 0x10, NDIS_STRING_CONST("IoRange")}, { CID_IRQ_NUMBER, 3, NDIS_STRING_CONST("IrqNumber")}, { -1,-1,NULL} };
可见这个全局变量设置了一些初始值,CONFIG_PARAMETER的结构体如下:
typedef struct _CONFIG_PARAMETER { U32 uId; U32 uDefValue; NDIS_STRING szName; } CONFIG_PARAMETER, *PCONFIG_PARAMETER;
所以全局变量设置的初始值的第一个参数表示ID(Device类的成员数组m_szConfigures[]的索引ID),第二个参数是默认值,第三个是名字,名字是用来读取注册表时用的。
EDeviceValidateConfigurations的分析,主要见语句的注释:
void C_DM9000::EDeviceValidateConfigurations(void) {RETAILMSG(1,(TEXT("C_DM9000::EDeviceValidateConfigurations\r\n"))); NDIS_HANDLE hndis = m_pUpper->GetNdisHandle(); // validate slot number if( (m_szConfigures[CID_IO_BASE_ADDRESS] == -1) || (m_szConfigures[CID_IRQ_NUMBER] == -1) ) THROW(()); m_szCurrentSettings[SID_GEN_TRANSMIT_BUFFER_SPACE] = //1514*32 = 48448 m_szConfigures[CID_TXBUFFER_NUMBER]//读注册表得到值为0x20=32 * ETH_MAX_FRAME_SIZE;//1514 m_szCurrentSettings[SID_GEN_RECEIVE_BUFFER_SPACE] = //48448 m_szConfigures[CID_RXBUFFER_NUMBER]//读注册表:0x20 * ETH_MAX_FRAME_SIZE;//1514 m_szConfigures[CID_CHECK_FOR_HANG_PERIOD] = 3;//hang 周期 m_szConfigures[CID_IRQ_GEN_TYPE] = NdisInterruptLatched; m_szConfigures[CID_IRQ_SHARED] = TRUE; m_szConfigures[CID_IRQ_LEVEL] = 0x0F; m_szConfigures[CID_INTERFACE_TYPE] = NdisInterfaceIsa; m_szConfigures[CID_BUS_MASTER] = FALSE; // set receive mode // <5> discard long packet // <4> discard CRC error packet // <0> rx enable m_szCurrentSettings[SID_OP_MODE] = MAKE_MASK3(5,4,0);//工作模式,0011 0001=0x31 m_szCurrentSettings[SID_802_3_MAXIMUM_LIST_SIZE] = DM9_MULTICAST_LIST; //64 }
DATA_BLOCK的详细说明
在Driver.h中有定义:
typedef struct _DATA_BLOCK { CQUEUE_GEN_HEADER Header; unsigned char Buffer[DRIVER_BUFFER_SIZE]; } DATA_BLOCK, *PDATA_BLOCK;
CQUEUE_GEN_HEADER结构体在common.h中有定义:
typedef struct _CQUEUE_GEN_HEADER { struct _CQUEUE_GEN_HEADER *pNext; U32 uFlags; PVOID pPacket; U16 nReserved; U16 nLength; } CQUEUE_GEN_HEADER, *PCQUEUE_GEN_HEADER;
DM9000A的初始化函数C_DM9000::EDeviceInitialize()的详细分析:
这个函数在文件Dm9isa.cpp中;前几句代码代码如下:
// reset member varialbes m_uLastAddressPort = (U32)-1; DeviceWritePort(0x1f, 0x00);//General purpose Register ( 1FH ) NdisStallExecution(20); // software reset the device DeviceWritePort(DM9_NCR, 0x03);// Network Control Register (00H) NdisStallExecution(20); DeviceWritePort(DM9_NCR, 0x03); // Network Control Register (00H) NdisStallExecution(20);
分析:DM9000A的General
purpose Register ( 1FH )寄存器,即GPIO引脚状态寄存器,这里设置power
up PHY,后面两句是软件复位DM9000A,两次复位以保证确实达到复位目的。
继续看,代码如下:
// read the io orgnization // ISR<7:6> == x1, dword // ISR<7:6> == 0x, word // ISR<7:6> == 10, byte mode val = DeviceReadPort(DM9_ISR); if(val & MAKE_MASK(6)) { m_nIoMode = DWORD_MODE; m_nIoMaxPad = 3; } else if(!(val & MAKE_MASK(7))) { m_nIoMode = WORD_MODE; m_nIoMaxPad = 1; } else { m_nIoMode = BYTE_MODE; m_nIoMaxPad = 0; }
以上是ISR(FEH):中断状态寄存器(Interrupt
Status Register)的读取来判断m_nIoMode,我认为其实这里是DM9000的程序,不应该是DM9000A的程序,因为DM9000A的datasheet中ISR寄存器bit
6 是reserved的,而查看DM9000的资料,它用bit
[7:6]来判断IO口模式,不过我们这里程序能用,是因为DM9000A的bit
7 == 0则是word类型,bit
7== 1则是byte类型,而这里正好是是word类型,个人认为如此。
继续看,代码如下:
DeviceWritePort(DM9_GPCR, (1<<0)); DeviceWritePort(DM9_GPR, 0x00);
这两句也是对应DM9000而不是DM9000A,第一句设置GPIO控制寄存器让GPIO0为输出,第二句让其输出0,而DM9000A的GPCR寄存器bit
0 本身就是只读,且为1,GPR寄存器bit
0 设置为0是powe up PHY,设置为1是power
down PHY;这里按照DM9000设置GPIO0,目的一样,设置为power
up PHY,即使能内部PHY。
继续看,代码如下:
DeviceWritePort(DM9_NSR, 0x00);
设置网络状态寄存器NSR(01h)Network Status Register,设置一些初始值:
7:SPEED:媒介速度,在内部PHY模式下,0为100Mbps,1为10Mbps。当LINKST=0时,此位不用。
6:LINKST:连接状态,在内部PHY模式下,0为连接失败,1为已连接。
5:WAKEST:唤醒事件状态。读取或写1将清零该位。不受软件复位影响。
4:保留。
3:TX2END:TX(发送)数据包2完成标志,读取或写1将清零该位。数据包指针2传输完成。
2:TX2END:TX(发送)数据包1完成标志,读取或写1将清零该位。数据包指针1传输完成。
1:RXOV:RX(接收)FIFO(先进先出缓存)溢出标志。
0:保留。
继续看,代码如下:
DeviceWritePort(DM9_IMR, (1<<7));
中断屏蔽寄存器(FEh)IMR(Interrupt Mask Register)(初始默认值为0)各bit如下:
7:PAR:1使能指针自动跳回。当SRAM的读、写指针超过SRAM的大小时,指针自动跳回起始位置。需要驱动程序设置该位,若设置则REG_F5(MDRAH)将自动位0CH。
6:保留。
5:LNKCHGI:1使能连接状态改变中断。
4:UDRUNI:1使能传输“Underrun”中断。
3:ROOI:1使能接收溢出计数器溢出中断。
2:ROI:1使能接收溢出中断。
1:PTI:1使能数据包传输终端。
0:PRI:1使能数据包接收中断。
继续看,代码如下:
m_nMaxTxPending = (DeviceReadPort(DM9_CHIPREV) >= 0x10)?2:1; m_nTxPendings = 0;
CHIPREV(2ch)芯片修订版本,根据数据手册,DM9000A得到的值为0x18,所以m_nMaxTxPending
== 2。
到此位置,DM9000A的初始化配置EDeviceInitialize就完成了。
未完待续,上述中代码类型有些不对,修改太累,凑合看吧
相关文章推荐
- wince下DM9000A网卡驱动移植及学习总结---3
- wince下DM9000A网卡驱动移植及学习总结---1
- wince下DM9000A网卡驱动移植及学习总结---4
- AM335x(TQ335x)学习笔记——Nand&&网卡驱动移植
- wince下Dm9000A驱动调试总结----转自巴乔 .
- 2440下vxWorks DM9000A网卡驱动移植
- AM335x(TQ335x)学习笔记——Nand&&网卡驱动移植
- AM335x(TQ335x)学习笔记——Nand&&网卡驱动移植
- tiny4412学习(三)之移植linux-4.x驱动(1)支持网卡驱动
- wince下Dm9000A驱动调试总结
- 友坚U-boot-1.1.6学习及移植dm9000a驱动
- AM335x(TQ335x)学习笔记——Nand&&网卡驱动移植
- 今天移植了2.6.25的网卡驱动 总结一下
- wince下Dm9000A驱动调试总结----转自巴乔
- Linux驱动修炼之道-DM9000A网卡驱动框架源码分析
- rtl8188eu无线网卡驱动移植
- RIS镜像中添加网卡和RAID卡驱动方法及实践经验总结
- mcp2515带spi的can驱动移植总结
- (Macbook Air)BCM4360网卡Linux(Ubuntu/Fedora)驱动安装总结
- linux驱动移植经验总结