s3c2440平台linux2.6.32双网卡驱动
网络设备驱动编写 前段时间由于一个工业以太网的需求,需要在S3C2440平台上实现双网卡。其中由DM9000构成的100M网络用于满足作为WEB服务器的需求,另外一路由于直接与设备相连,对带宽要求较低,另外为了接口方便选用了SPI接口的ENC28J60以实现一个10M的网络,并工作于混杂模式。 Linux下的网络设备驱动位于/linux/driver/net目录下,其中已经包含了许多常用的以太网控制器的驱动,包括CS8019、DM9000等。由于ENC28J60是新出的一款专为单片机设计的以太网控制器,所以在现有内核驱动中还尚未得到支持,那么重写ENC28J60设备驱动成了必然的选择。首先无论是linux下的网卡驱动还是单片程序的裸机驱动,有几个地方是完全相同的,而且是必须做的工作: l以太网控制器正确初始化。对ENC28J60而言其初始化的过程包括: 1)S3C2440SPI接口的初始化 2)ENC28J60复位 3)发送控制寄存器、接收控制寄存器、发送和接收FIFO的设置以及物理地址的设置 l以太网控制器发送数据。 l接收数据函数,其中可能采用中断接收或查询方式。 关于底层的数据收发函数的实现,由于网上有较多的单片机代码可以参考这里就不详述了。有了最底层的send、receive函数以后,接下来的问题就是如何将这些函数与linux内核联系起来,这也将是网络设备驱动与裸机驱动最本质的区别。接下来就以表格的形式列出网络驱动和字符设备的区别,如下表: 上面将网络设备驱动和字符设备驱动做了比较,那么接下来我们不妨借鉴设计字符设备驱动的方法来重写网络设备驱动。 首先还是以模块的形式来加载,那么module_init是必须要做的工作,重写如下: module_init(enc28j60_init_module); module_exit(enc28j60_cleanup); MODULE_AUTHOR("whut"); MODULE_DESCRIPTION("NE28j60networkdriver"); MODULE_LICENSE("GPL"); 接下来就是编写enc28j60_init_module的实现了, 805 int__initenc28j60_init_module(void) 806 { 807 intresult; 808 base_addr=ioremap(S3C2410_PA_SPI,0x18); 809 PRINTK("iomapsucess!!/n"); 810 init_SPI();//25M 811 //init_SPI(BandRate_10Mbps); 812 strcpy(enc28j60.name,"eth%d"); 813 //enc28j60.name="NET28J60"; 814 enc_device_init(); //enc28j60初始化 815 if((result=register_netdev(&enc28j60))){ 816 PRINTK("enc28j60:Error%dinitializingenc28j60baseddevice",result); 817 returnresult; 818 } 819 return0; 820 } 810行就是初始化S3C2440中SPI模块以适应ENC28J60要求 814行明显将要做enc28j60的硬件初始化,以及我们刚才所说的structnet_device机构的初始化,以便815行的register_netdev调用。 815行注册成功过后,设备就进入了正式工作状态,同时proc/device中将出现他的身影。 [enc_device_init()] 717 voidenc_device_init(void) 718 { 719 720 enc28j60WriteOp(ENC28J60_SOFT_RESET,0,ENC28J60_SOFT_RESET); 721 udelay(1000);//复位后的延时 722 723 enc28j60PhyWrite(PHLCON,0x3412); 724 725 enc28j60SetBank(ECON1); 726 727 NextPacketPtr=RXSTART_INIT; 728 enc28j60Write(ERXSTL,RXSTART_INIT&0xFF); 729 enc28j60Write(ERXSTH,RXSTART_INIT>>8); 730 //setreceivepointeraddress 731 enc28j60Write(ERXRDPTL,RXSTART_INIT&0xFF); 732 enc28j60Write(ERXRDPTH,RXSTART_INIT>>8); 733 734 enc28j60Write(ERDPTL,RXSTART_INIT&0xFF); 735 enc28j60Write(ERDPTH,RXSTART_INIT>>8); 736 //enc28j60Write(ERXWRPTL,RXSTART_INIT&0xFF);//setthewrpointerorreceivearea 737 //enc28j60Write(ERXWRPTH,RXSTART_INIT>>8); 738 //enc28j60Write(ERXRDPTL,RXSTART_INIT&0xFF); 739 //enc28j60Write(ERXRDPTH,RXSTART_INIT); 740 741 //setreceivebufferend: 742 //ERXNDdefaultsto0x1FFF(endofram) 743 enc28j60Write(ERXNDL,RXSTOP_INIT&0xFF); 744 enc28j60Write(ERXNDH,RXSTOP_INIT>>8); 745 //settransmitbufferstart 746 //ETXSTdefaultsto0x0000(beginngingofram) 747 enc28j60Write(ETXSTL,TXSTART_INIT&0xFF); 748 enc28j60Write(ETXSTH,TXSTART_INIT>>8); 749 750 PRINTK("bank2enc28j60initprocess!/n"); 751 752 //dobank2stuff 753 enc28j60Write(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); 754 //bringMACoutofreset 755 enc28j60Write(MACON2,0x00); 756 //enableautomaticpaddingandCRCoperations 757 //enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);//bsg 758 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX);//qsg 759 enc28j60Write(MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); 760 //setinter-framegap(non-back-to-back) 761 enc28j60Write(MAIPGL,0x12); 762 enc28j60Write(MAIPGH,0x0C); 763 //setinter-framegap(back-to-back) 764 //enc28j60Write(MABBIPG,0x12);//bsg半双工 765 enc28j60Write(MABBIPG,0x15);//qsg全双工 766 767 enc28j60PhyWrite(PHCON1,0x0100);//qsg 768 769 //Setthemaximumpacketsizewhichthecontrollerwillaccept 770 enc28j60Write(MAMXFLL,MAX_FRAMELEN&0xFF); 771 enc28j60Write(MAMXFLH,MAX_FRAMELEN>>8); 772 773 PRINTK("bank3enc28j60initprocess!/n"); 774 //dobank3stuff 775 //writeMACaddress 776 //NOTE:MACaddressinENC28J60isbyte-backward 777 enc28j60Write(MAADR5,ENC28J60_MAC0); 778 enc28j60Write(MAADR4,ENC28J60_MAC1); 779 enc28j60Write(MAADR3,ENC28J60_MAC2); 780 enc28j60Write(MAADR2,ENC28J60_MAC3); 781 enc28j60Write(MAADR1,ENC28J60_MAC4); 782 enc28j60Write(MAADR0,ENC28J60_MAC5); 783 784 //noloopbackoftransmittedframes 785 PRINTK("phyenc28j60initprocess!/n"); 786 enc28j60PhyWrite(PHCON2,PHCON2_HDLDIS); 787 //enableinterrutps 788 //enablepacketreception 789 790 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN); 791 //enc28j60Write(ERXFCON,0x00); 792 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIR,EIR_PKTIF|EIR_TXIF|EIR_RXERIF|EIR_TXERIF); 793 PRINTK("enablinginterupts!/n"); 794 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE); 795 796 //Reenablereceivelogic 797 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN); 798 PRINTK("enc28j60deviceinitialized/n"); 799 } 这段代码无疑是一段赤裸裸的裸机驱动程序,复位enc28j60、设置FIFO、设置发送接收控制寄存器。 硬件初始化了,但是structnet_device结构却迟迟未能出现,其实这里填充了一个structnet_device的init方法,这是在注册structnet_device的时候会提前调用的。 803structnet_deviceenc28j60={init:enc28j60_init}; 进入enc28j60_init函数… 659 int__initenc28j60_init(structnet_device*dev) 660 { 661 board_info_t*db; 662 u16temp; 663 db=(void*)(kmalloc(sizeof(*db),GFP_KERNEL)); 664 memset(db,0,sizeof(*db)); 665 dev->priv=db; 666 memset(dev->priv,0,sizeof(structnet_device_stats)); 667 dev->get_stats=get_stats; 668 enc_dev=dev; 669 dev->open=&enc28j60_open; 670 dev->stop=&enc28j60_release; 671 dev->hard_start_xmit=&enc28j60_xmit; 672 dev->tx_timeout=&net_timeout; 673 dev->watchdog_timeo=5*HZ; 674 dev->irq=IRQ_EINT0; 675 spin_lock_init(&lplock); 676 dev->dev_addr[0]=0x80;//0x11;i 677 dev->dev_addr[1]=0xe2; 678 dev->dev_addr[2]=0x66; 679 dev->dev_addr[3]=0x60; 680 dev->dev_addr[4]=0x00; 681 dev->dev_addr[5]=0x01; 682 nicSetMacAddress(dev->dev_addr); 683 { 684 u8checkaddr[6]; 685 nicGetMacAddress(checkaddr); 686 if(memcmp(checkaddr,dev->dev_addr,6)!=0) 687 { 688 kfree(dev->priv); 689 dev->priv=NULL; 690 PRINTK("enc28j60error!maybenoic(%2X:%2X:%2X:%2X:%2X:%2X)!/n",checkaddr[0],checkaddr[0],checkaddr[1],checkaddr[2],checkaddr[3],checkaddr[4],checkaddr[5]); 691 return-ENODEV; 692 } 693 } 694 PRINTK("setmacaddrsucess!/n"); 695 ether_setup(dev); 696 temp=enc28j60PhyRead(PHHID1); 697 if(temp==0x83) 698 PRINTK("EN28J60initok!/n"); 699 #ifdefDEBUG 700 PRINTK("NETID:%d/n",temp); 701 #endif 702 PRINTK("dev->hard_header_len:0x%02x/n",dev->hard_header_len); 703 //dobank0stuff 704 //initializereceivebuffer 705 //16-bittransfers,mustwritelowbytefirst 706 //setreceivebufferstartaddress 707 PRINTK("startingenc28j60initprocess!/n"); 708 return0; 709 } 这段就是对structnet_device的初始化了, 669 dev->open=&enc28j60_open; //打开网络设备,将会通过netif_start_queue(dev);启动接口的发送队列(允许它接受发送报文),一旦它准备好启动发送数据 670 dev->stop=&enc28j60_release; //关闭网络驱动,停止设备 671 dev->hard_start_xmit=&enc28j60_xmit;//发送数据包,对应裸机驱动的发送函数 dev->tx_timeout=&net_timeout; //发送超时时调用的处理函数 695行调用ether_setup(dev);对这个网络设备初始化为默认的以太网设备。 接下来就是到了open、send、receive的问题了。前面说过,对open来说就是初始化驱动需要支持的内容,比如定时器、工作队列、申请中断等等的初始化,并开启发送队列。 413 intenc28j60_open(structnet_device*dev) 414 { 415 intresult; 416 board_info_t*db=(board_info_t*)dev->priv; 417 PRINTK("enc28j60open!/n"); 418 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIE,EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE); 419 //注册中断 420 if(request_irq(dev->irq,&enc28j60_Interrupt,IRQ_TYPE_EDGE_FALLING,"enc28j60",dev))//申请中断 421 { 422 PRINTK("enc28j60IRQ%drequestfail!/n",dev->irq); 423 return-EAGAIN; 424 } 425 #ifdefDEBUG 426 else 427 PRINTK("requestirqsucess!!!/n"); 428 #endif 429 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE); 430 //打开EIE中断 431 /*setandactiveatimerprocess*/ 432 init_timer(&db->timer); 433 db->timer.expires =DMFE_TIMER_WUT*2; 434 db->timer.data =(unsignedlong)dev; 435 db->timer.function =&enc_timer; 436 //add_timer(&db->timer);//打开定时中断 437 db->tx_pkt_cnt=0; 438 db->queue_pkt_len=0; 439 dev->trans_start=0; 440 netif_start_queue(dev); 441 inti; 442 for(i=0;i<30;i++) 443 enc28j60_send(); 444 return0; 445 } 关于发送的实现就更加简单了,只需将裸机驱动中的发送数据替换成structsk_buff中的内容即可。发送完成后将structsk_buff释放。 487 staticintenc28j60_xmit(structsk_buff*skb,structnet_device*dev) 488 { 489 unsignedcharstat; 490 //structnet_local*lp=(structnet_local*)dev->priv; 491 spin_lock_irq(&lplock);//互斥锁 492 netif_stop_queue(dev);//停止网络发送 493 #ifdefDEBUG 494 PRINTK("EN28J60send!!/n"); 495 #endif 496 /*Disableallinterrupt*/ 497 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIE,EIE_INTIE); 498 enc28j60Write(EWRPTL,TXSTART_INIT);//28j60缓冲区写入的位置 499 enc28j60Write(EWRPTH,TXSTART_INIT>>8); 500 enc28j60Write(ETXSTL,TXSTART_INIT);//28j60缓冲区开始位置 501 enc28j60Write(ETXSTH,TXSTART_INIT>>8); 502 //SettheTXNDpointertocorrespondtothepacketsizegiven 503 enc28j60Write(ETXNDL,(TXSTART_INIT+skb->len));//skb->len发送数据的长度 504 enc28j60Write(ETXNDH,(TXSTART_INIT+skb->len)>>8); 505 stat=enc28j60Read(ESTAT); 506 if(stat&2)//检查是否有发送错误 507 { 508 PRINTK("senderro!/n"); 509 spin_unlock_irq(&lplock); 510 return1; 511 } 512 //writeper-packetcontrolbytelht 513 enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM,0,0x00);//useMCON3参考42页 514 //copythepacketintothetransmitbuffer 515 enc28j60WriteBuffer(skb->len,skb->data); 516 //udelay(100); 517 //sendthecontentsofthetransmitbufferontothenetwork 518 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS); 519 udelay(400);//可以调试,有可能影响稳定性可以更改 520 spin_unlock_irq(&lplock); 521 dev->trans_start=jiffies;//加入时间戳 522 dev_kfree_skb(skb);//释放空间 523 //netif_wake_queue(dev); 524 udelay(200);//可以调试,有可能影响稳定性可以更改 525 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE); 526 return0; 527 } 528 529 530 /*****************************************************************************/ 531 staticintenc28j60_send(void) 532 { 533 unsignedcharstat,i; 534 charbuff[70]; 535 unsignedchartx_length=69; 536 for(i=0;i<70;i++) 537 buff[i]=i; 538 spin_lock_irq(&lplock);//互斥锁 539 PRINTK("EN28J60send!!/n"); 540 /*Disableallinterrupt*/ 541 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIE,EIE_INTIE); 542 //mc=enc28j60Read(MAADR5); 543 //printk("mac5=%x/n",mc); 544 //Setthewritepointertostartoftransmitbufferarea 545 enc28j60Write(EWRPTL,TXSTART_INIT);//28j60缓冲区写入的位置 546 enc28j60Write(EWRPTH,TXSTART_INIT>>8); 547 548 enc28j60Write(ETXSTL,TXSTART_INIT);//28j60缓冲区开始位置 549 enc28j60Write(ETXSTH,TXSTART_INIT>>8); 550 //SettheTXNDpointertocorrespondtothepacketsizegiven 551 enc28j60Write(ETXNDL,(TXSTART_INIT+tx_length));//skb->len发送数据的长度 552 enc28j60Write(ETXNDH,(TXSTART_INIT+tx_length)>>8); 553 stat=enc28j60Read(ESTAT); 554 //printk("ESTAT=%x/n",stat); 555 if(stat&2)//检查是否有发送错误 556 { 557 PRINTK("senderro!/n"); 558 spin_unlock_irq(&lplock); 559 return1; 560 } 561 //writeper-packetcontrolbytelht 562 enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM,0,0x06);//useMCON3参考42页 563 //copythepacketintothetransmitbuffer 564 enc28j60WriteBuffer(tx_length,buff); 565 //udelay(100); 566 //sendthecontentsofthetransmitbufferontothenetwork 567 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS); 568 udelay(400);//可以调试,有可能影响稳定性可以更改 569 spin_unlock_irq(&lplock); 570 //netif_wake_queue(dev); 571 udelay(200);//可以调试,有可能影响稳定性可以更改 572 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE); 573 return0; 574 } 上面代码与裸机驱动差异只在structsk_buff上面,所以实现起来也就较为简单了。 最后,接收的代码就出现在中断处理函数中了,收到数据需要重新申请structsk_buff,然后根据接收的内容进行相应的填充,最后由netif_rx(skb);提交给网络层。 577 staticirqreturn_tenc28j60_Interrupt(intirq,void*dev_id) 578 { 579 structnet_device*dev=dev_id; 580 structsk_buff*skb; 581 u16rxstat; 582 u16len=0; 583 u16status,PKCNT; 584 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIE,EIE_INTIE);//disableirq 585 status=enc28j60Read(EIR); 586 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIR,status); 587 if(status&EIR_TXIF)//发送中断 588 { 589 #ifdefDEBUG 590 PRINTK("txpacketok!!/n"); 591 #endif 592 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIR,EIR_TXIF); 593 netif_wake_queue(dev); 594 } 595 596 if(status&EIR_PKTIF)//接收中断 597 { 598 do{ 599 NextPacketPtr=enc28j60ReadOp(ENC28J60_READ_BUF_MEM,0);//下个数据包起始地址参考45页 600 NextPacketPtr|=enc28j60ReadOp(ENC28J60_READ_BUF_MEM,0)<<8; 601 //printk("NextPacketPtr=%x/n",NextPacketPtr); 602 //re adthepacketlength 603 len=enc28j60ReadOp(ENC28J60_READ_BUF_MEM,0);//数据包长度 604 len|=enc28j60ReadOp(ENC28J60_READ_BUF_MEM,0)<<8; 605 606 // printk("len=%x/n",len); 607 608 rxstat=enc28j60ReadOp(ENC28J60_READ_BUF_MEM,0); 609 rxstat|=enc28j60ReadOp(ENC28J60_READ_BUF_MEM,0)<<8; 610 / 611 612 skb=dev_alloc_skb(len+2); 613 skb->dev=dev; 614 skb_reserve(skb,2); 615 skb_put(skb,len); 616 //copythepacketfromthereceivebuffer 617 enc28j60ReadBuffer(len,skb->data); 618 //MovetheRXreadpointertothestartofthenextreceivedpacket 619 //Thisfreesthememorywejustreadout 620 #ifdefDEBUG 621 PRINTK("rxsucess!/n") 622 PRINTK("length=%d/n",len); 623 u16i; 624 for(i=0;i<len;i++) 625 PRINTK("%2X",(u8)(*(skb->data+i))); 626 PRINTK("/n"); 627 #endif 628 enc28j60Write(ERXRDPTL,(NextPacketPtr)); 629 enc28j60Write(ERXRDPTH,(NextPacketPtr)>>8); 630 enc28j60Write(ERDPTL,(NextPacketPtr)); 631 enc28j60Write(ERDPTH,(NextPacketPtr)>>8);//不用.可以自动变化ECON2.AUTOINC置位 632 633 //decrementthepacketcounterindicatewearedonewiththispacket 634 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,ECON2,ECON2_PKTDEC); 635 skb->protocol=eth_type_trans(skb,dev); 636 //skb->ip_summed=CHECKSUM_UNNECESSARY; 637 netif_rx(skb);//数据交给上层网络 638 //udelay(100); 639 dev->last_rx=jiffies; 640 PKCNT=enc28j60Read(EPKTCNT);//缓冲区还有多少包? 641 }while(PKCNT!=0);// 642 } 643 if(status&EIE_TXERIE) 644 { 645 PRINTK("TXerro/n"); 646 enc_device_init(); 647 netif_wake_queue(dev); 648 } 649 if(status&EIE_RXERIE) 650 { 651 //enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR,EIR,EIE_RXERIE); 652 PRINTK("RXerro/n");; 653 enc_device_init(); 654 netif_wake_queue(dev); 655 } 656 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE); 657 returnIRQ_HANDLED; 658 } 以上就是一个网络设备驱动几个比较关键的环节了,最后的工作就是简单修改makefile和kconfig的内容使其支持该驱动即可。以模块形式加载以后如果无异常,在proc/device中将出现设备的名字。通过ifconfig+设备名+ip。将激活网络设备。最后注意两张网卡不能处于同一网段,否则无法工作。一切完了之后,就可以用PING命令来测试网络连接了。 其实从上面的实现过程中我们不难发现,网络设备驱动与裸机驱动最大的区别就是需要填充几个网络层的接口函数,并且要通过相应的注册函数将内核指定的数据结构注册进内核。这一过程恰恰和字符设备的几个过程惊人的相似,所以参考最熟悉的字符设备驱动编写的几个流程,来编写网络设备驱动就异常清晰了。 源码链接/source/2759030