111122-1257
2-3 dm9000FuncTable
这个结构体如下:
static NET_FUNCS dm9000FuncTable = {
(FUNCPTR) dm9000Start, /* Function to start the device. */ (FUNCPTR) dm9000Stop, /* Function to stop the device. */
(FUNCPTR) dm9000Unload, /* Unloading function for the driver. */ (FUNCPTR) dm9000Ioctl, /* Ioctl function for the driver. */ (FUNCPTR) dm9000Send, /* Send function for the driver. */ (FUNCPTR) dm9000MCastAdd, /* Multicast add function for the */ /* driver. */
(FUNCPTR) dm9000MCastDel, /* Multicast delete function for */ /* the driver. */
(FUNCPTR) dm9000MCastGet, /* Multicast retrieve function for */ /* the driver. */
(FUNCPTR) dm9000PollSend, /* Polling send function */ (FUNCPTR) dm9000PollRcv, /* Polling receive function */
endEtherAddressForm, /* put address info into a NET_BUFFER */ endEtherPacketDataGet, /* get pointer to data in NET_BUFFER */ endEtherPacketAddrGet /* Get packet addresses. */ };
上面说过,这个结构体主要涉及dm9000Start,dm9000Ioctl,dm9000Send,其他的都好操作,而dm9000Ioctl关键,但是涉及的源码无需改动就可,通用的。
2-4.dm9000Start
源码如下:
static STATUS dm9000Start( END_DEVICE * pDrvCtrl ) /* device ID */ {
STATUS result;
unsigned char REG05; /*sailot test*/ cpuForDM9000Init(); /*sailor*/
DRV_LOG (DRV_DEBUG_LOAD, \ intConnect(INUM_TO_IVEC(pDrvCtrl->ivec),(VOIDFUNCPTR)dm9000Int,(int)(pDrvCtrl)) if (result == ERROR){ printf(\ return ERROR; } printf(\
11
111122-1257
result = intEnable((int)pDrvCtrl->ivec); if (result == ERROR){ printf(\ return ERROR; }
DRV_LOG (DRV_DEBUG_LOAD, \ /* Activate DM9000 */
iow(pDrvCtrl, 0x05, DM9000_REG05 ); /* RX enable */
iow(pDrvCtrl, 0xff, DM9000_REGFF ); /* Enable TX/RX interrupt mask */ DRV_LOG (DRV_DEBUG_LOAD, \ return (OK); }
其中cpuForDM9000Init(); /*sailor*/涉及对cpu中断寄存器的初始化,要根据硬件寄存器来设置
void cpuForDM9000Init(void){ immap = 0xe0000000; /*config the io pin to the interrupt*/ ic_sicrh = ( volatile unsigned int *)(immap + 0x118); *ic_sicrh &= ~0x4000; /*high-to-low gernerate the interrupt*/ ic_secnr = ( volatile unsigned int *)(immap + 0x73c); *ic_secnr |= 0x4000; /*unmask the interrupts*/ ic_semsr = ( volatile unsigned int *)(immap + 0x738); *ic_semsr |= 0x40000000; }
这个代码操作寄存器并把中断硬件层打卡,而在start函数中intEnable((int)pDrvCtrl->ivec)来打开操作系统软件层。
在start函数中把dm9000Int注册到系统中。这就是中断函数
2-5.dm9000Ioctl
这个函数无需修改,直接移植系统原来的就行
这个函数主要就是返回一些信息,包括网卡的mac地址,mblk信息等,无需修改就可移植到上面。
2.6. dm9000Send
static STATUS dm9000Send( END_DEVICE * pDrvCtrl, M_BLK_ID pMblk ) { int oldLevel = 0;
BOOL freeNow = TRUE; PKT skb; int len;
12
111122-1257
int i;
DRV_LOG (DRV_DEBUG_LOAD, \4, 5, 6);
END_TX_SEM_TAKE (&pDrvCtrl->end, WAIT_FOREVER); oldLevel = intLock (); /* protect dm9000Int */ /* TODO - initiate device transmit */
len = netMblkToBufCopy (pMblk, (char*)(pDrvCtrl->txBuf), NULL); DRV_LOG (DRV_DEBUG_LOAD, \ SEND_DEBUG(\ for (i=0;i SEND_DEBUG(\ len = max (len, ETHERSMALL); skb.len = len; //后面数据部分的长度 skb.pData = (char*)pDrvCtrl->txBuf; if( dmfe_start_xmit( &skb, pDrvCtrl ) ) { DRV_LOG (DRV_DEBUG_STATUS, \ freeNow = FALSE; intUnlock (oldLevel); netMblkClChainFree (pMblk); if (!((pDrvCtrl->flags) & DM9000_POLLING)) END_TX_SEM_GIVE (&pDrvCtrl->end); return(END_ERR_BLOCK); } intUnlock (oldLevel); END_TX_SEM_GIVE (&pDrvCtrl->end); END_ERR_ADD (&pDrvCtrl->end, MIB2_OUT_UCAST, +1); netMblkClChainFree (pMblk); DRV_LOG (DRV_DEBUG_LOAD, \ return (OK); } 函数中len = netMblkToBufCopy (pMblk, (char*)(pDrvCtrl->txBuf), NULL);把发送的信息从pMblk中给拷贝到pDrvCtrl->txBuf,而返回拷贝的大小len,后把这些信息付给skb,而skb是封装的临时结构体,如下: typedef struct { int len; char * pData; 13 111122-1257 } PKT; /* A dummy DMA data packet */ 在调试的时候可以把打印信息放在这里,打印出发送的数据包,即上面代码的 for (i=0;i 如果程序没有错误,这个地方会出来打印信息,并且在用抓包工具可以抓到和打印信息相同的包信息,就说明发送成功了。 后面调用dmfe_start_xmit( &skb, pDrvCtrl ),把数据发送出去,这其中是最关键的代码,如下: static int dmfe_start_xmit( PKT *skb, END_DEVICE *dev) { char *data_ptr; int i, oldv; unsigned char imr=0; int tmplen; UWORD tmpdata; UCHAR regsave; DRV_LOG (DRV_DEBUG_TX, \ DRV_LOG (DRV_DEBUG_TX, \ if( (skb->len == 0) || (skb->pData == NULL) ){ DRV_LOG (DRV_DEBUG_STATUS,\ return 2; } /* Resource flag check *///这个部分是用来确定发送中断是否相应,如果连续2次没响应的话,发送就是错误,直接返回 if( dev->tx_pkt_cnt >= 2){ DRV_LOG (DRV_DEBUG_STATUS,\ regsave= ior( dev,2); return 1; } /* Move data to DM9000 TX RAM */ data_ptr = (char *)skb->pData; #if 0 //这个部分为调试信息,就是打印发送包的内容信息 SEND_DEBUG(\ for (i=0;i outb( dev->io_addr,0xf8 ); 14 111122-1257 if( dev->io_mode == 1 ) { /* Byte mode */ for( i = 0; i < skb->len; i++ ) outb( dev->io_data,(data_ptr[i] & 0xff) ); } Else //进入2,因为初始化时就设置为了0,为字传送,也就是2,依次接收2个字节 { SEND_DEBUG(\ /* Word mode */ tmplen = (skb->len + 1) / 2; for( i = 0; i < tmplen; i++ ){ outb( dev->io_data, SWAP16(((unsigned short *)data_ptr)[i])); if(((i+1) % 10)==0) SEND_DEBUG(\ } } SEND_DEBUG(\ /* TX control: First packet immediately send, second packet queue */ if (dev->tx_pkt_cnt == 0) { /* First Packet */ dev->tx_pkt_cnt ++;//在这里应该把发送计数器++,在中断中来--,测试中断响应 /* Set TX length to DM9000 */ iow(dev, 0xfc, skb->len & 0xff );//把发送的数据长度写进dm9000对应的reg,这个地方,我感觉应该是在上面依次发送len长度,这时数据并没有发送出去,只是暂存在内部的缓冲区中,而这个地方是把长度放到寄存器中,然后dm9000来把缓冲区中的数据发送出去。 iow(dev, 0xfd, (skb->len >> 8) & 0xff ); /* Issue TX polling command */ iow(dev, 0x2, 0x01 ); /* Cleared after TX complete */ } else /* Second packet */ //这是2个包处理的情况(一般不会发生) { dev->queue_pkt_len = skb->len; dev->tx_pkt_cnt ++; } return 0; } 上面代码outb( dev->io_data, SWAP16(((unsigned short *)data_ptr)[i]));这部分很重要,主要是用来对发送的数据进行16位高低转换,也就是反序,代码如下: static __inline__ unsigned short SWAP16(unsigned short x) { unsigned short y; //SEND_DEBUG(\ y=(((x)&0xff)<<8)|(((x)&0xff00)>>8); //这个算法比较经典 SEND_DEBUG(\ 15