s3c2440平台linux2.6.32双网卡驱动
网络设备驱动编写
前段时间由于一个工业以太网的需求,需要在S3C2440平台上实现双网卡。其中由DM9000构成的100M网络用于满足作为WEB服务器的需求,另外一路由于直接与设备相连,对带宽要求较低,另外为了接口方便选用了SPI接口的ENC28J60以实现一个10M的网络,并工作于混杂模式。
Linux下的网络设备驱动位于/linux/driver/net目录下,其中已经包含了许多常用的以太网控制器的驱动,包括CS8019、DM9000等。由于ENC28J60是新出的一款专为单片机设计的以太网控制器,所以在现有内核驱动中还尚未得到支持,那么重写ENC28J60设备驱动成了必然的选择。首先无论是linux下的网卡驱动还是单片程序的裸机驱动,有几个地方是完全相同的,而且是必须做的工作:
l 以太网控制器正确初始化。对ENC28J60而言其初始化的过程包括:
1) S3C2440 SPI接口的初始化
2) ENC28J60复位
3) 发送控制寄存器、接收控制寄存器、发送和接收FIFO的设置以及物理地址的设置
l 以太网控制器发送数据。
l 接收数据函数,其中可能采用中断接收或查询方式。
关于底层的数据收发函数的实现,由于网上有较多的单片机代码可以参考这里就不详述了。有了最底层的send、receive函数以后,接下来的问题就是如何将这些函数与linux内核联系起来,这也将是网络设备驱动与裸机驱动最本质的区别。接下来就以表格的形式列出网络驱动和字符设备的区别,如下表:
类型 | 字符设备 | 网络设备 |
设备的数据结构 | struct cdev | struct net_device |
设备数据结构的分配 | cdev_alloc(); | struct net_device *alloc_netdev(int sizeof_priv, constchar*name, void(*setup)(struct net_device)); |
设备数据结构 初始化 | void cdev_init(struct cdev*dev, structfile_operations *f_ops) | 对于以太网设备可以通过ether_setup(struct net_deice *dev)函数进行以太网的部分缺省设置,其他异构网络需要根据实际情况设置struct net_deice |
驱动注册 | int cdev_add(struct cdev*dev,dev_t num,unsigned int count);
|
int register_netdev(struct net_device *dev)设备注册之前必须保证网卡初始化可用。 |
设备操作方法 | 封装在struct file_operations中,包括open、write、read等 | 封装在struct net_device_ops中, ndo_open、ndo_stop、ndo_start_xmit ndo_tx_timeout、ndo_do_ioctl、 ndo_set_mac_address等 |
设备号 | 分为主、次设备号,访问通过设备节点完成 |
无主、次设备号及设备文件 |
获取上层数据 | 使用copy_from_user等从用户空间获取数据 | 通过读取网络层传递的struct sk_buff结构获取待发送数据,并释放struct sk_buff结构 |
向上层提交数据 | 使用copy_to_user等将内核空间数据提交至用户空间 | 将接受的数据封装在申请的struct sk_buff结构中,并使用int netif_rx(struct sk_buff *skb)将数据提交至网络层 |
上面将网络设备驱动和字符设备驱动做了比较,那么接下来我们不妨借鉴设计字符设备驱动的方法来重写网络设备驱动。
首先还是以模块的形式来加载,那么module_init是必须要做的工作,重写如下:
module_init(enc28j60_init_module);
module_exit(enc28j60_cleanup);
MODULE_AUTHOR("whut");
MODULE_DESCRIPTION("NE28j60 network driver");
MODULE_LICENSE("GPL");
接下来就是编写enc28j60_init_module的实现了,
805 int __init enc28j60_init_module (void)
806 {
807 int result;
808 base_addr=ioremap(S3C2410_PA_SPI,0x18);
809 PRINTK("io map sucess!!/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 %d initializing enc28j60 based device",result);
817 return result;
818 }
819 return 0;
820 }
810行就是初始化S3C2440中SPI模块以适应ENC28J60要求
814行明显将要做enc28j60的硬件初始化,以及我们刚才所说的struct net_device机构的初始化,以便815行的register_netdev调用。
815行注册成功过后,设备就进入了正式工作状态,同时proc/device中将出现他的身影。
[enc_device_init()]
717 void enc_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 // set receive pointer address
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); //set the wr pointer or receive area
737 // enc28j60Write(ERXWRPTH,RXSTART_INIT>>8);
738 // enc28j60Write(ERXRDPTL,RXSTART_INIT&0xFF);
739 // enc28j60Write(ERXRDPTH,RXSTART_INIT);
740
741 // set receive buffer end:
742 // ERXND defaults to 0x1FFF (end of ram)
743 enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);
744 enc28j60Write(ERXNDH, RXSTOP_INIT>>8);
745 // set transmit buffer start
746 // ETXST defaults to 0x0000 (beginnging of ram)
747 enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);
748 enc28j60Write(ETXSTH, TXSTART_INIT>>8);
749
750 PRINTK("bank2 enc28j60 init process!/n");
751
752 // do bank 2 stuff
753 enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
754 // bring MAC out of reset
755 enc28j60Write(MACON2, 0x00);
756 // enable automatic padding and CRC operations
757 // enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); //b s g
758 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX); //q s g
759 enc28j60Write(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
760 // set inter-frame gap (non-back-to-back)
761 enc28j60Write(MAIPGL, 0x12);
762 enc28j60Write(MAIPGH, 0x0C);
763 // set inter-frame gap (back-to-back)
764 // enc28j60Write(MABBIPG, 0x12); // b s g 半双工
765 enc28j60Write(MABBIPG, 0x15); // q s g 全双工
766
767 enc28j60PhyWrite(PHCON1,0x0100); //q s g
768
769 // Set the maximum packet size which the controller will accept
770 enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);
771 enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8);
772
773 PRINTK("bank3 enc28j60 init process!/n");
774 // do bank 3 stuff
775 // write MAC address
776 // NOTE: MAC address in ENC28J60 is byte-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 // no loopback of transmitted frames
785 PRINTK("phy enc28j60 init process!/n");
786 enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);
787 // enable interrutps
788 // enable packet reception
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("enabling interupts!/n");
794 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE);
795
796 // Reenable receive logic
797 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
798 PRINTK("enc28j60 device initialized/n");
799 }
这段代码无疑是一段赤裸裸的裸机驱动程序,复位enc28j60、设置FIFO、设置发送接收控制寄存器。
硬件初始化了,但是struct net_device结构却迟迟未能出现,其实这里填充了一个struct net_device的init方法,这是在注册struct net_device的时候会提前调用的。
803 struct net_device enc28j60 = {init: enc28j60_init};
进入enc28j60_init函数…
659 int __init enc28j60_init (struct net_device *dev)
660 {
661 board_info_t *db;
662 u16 temp;
663 db=(void *)(kmalloc(sizeof(*db),GFP_KERNEL));
664 memset(db,0,sizeof(*db));
665 dev->priv = db ;
666 memset(dev->priv, 0, sizeof(struct net_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 u8 checkaddr[6];
685 nicGetMacAddress(checkaddr);
686 if(memcmp(checkaddr, dev->dev_addr, 6) != 0)
687 {
688 kfree(dev->priv);
689 dev->priv = NULL;
690 PRINTK("enc28j60 error! may be no ic(%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("set macaddr sucess!/n");
695 ether_setup(dev);
696 temp=enc28j60PhyRead(PHHID1);
697 if(temp==0x83)
698 PRINTK("EN28J60 init ok!/n");
699 #ifdef DEBUG
700 PRINTK("NET ID:%d/n",temp);
701 #endif
702 PRINTK("dev->hard_header_len: 0x%02x/n",dev->hard_header_len);
703 // do bank 0 stuff
704 // initialize receive buffer
705 // 16-bit transfers, must write low byte first
706 // set receive buffer start address
707 PRINTK("starting enc28j60 init process!/n");
708 return 0;
709 }
这段就是对struct net_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 int enc28j60_open (struct net_device *dev)
414 {
415 int result;
416 board_info_t * db = (board_info_t *)dev->priv;
417 PRINTK("enc28j60 open!/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("enc28j60 IRQ%d request fail!/n",dev->irq);
423 return -EAGAIN;
424 }
425 #ifdef DEBUG
426 else
427 PRINTK("request irq sucess!!!/n");
428 #endif
429 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE|EIE_TXIE|EIE_RXERIE|EIE_TXERIE);
430 //打开EIE中断
431 /* set and active a timer process */
432 init_timer(&db->timer);
433 db->timer.expires = DMFE_TIMER_WUT * 2;
434 db->timer.data = (unsigned long)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 int i;
442 for(i=0;i<30;i++)
443 enc28j60_send();
444 return 0;
445 }
关于发送的实现就更加简单了,只需将裸机驱动中的发送数据替换成struct sk_buff中的内容即可。发送完成后将struct sk_buff释放。
487 static int enc28j60_xmit (struct sk_buff *skb, struct net_device *dev)
488 {
489 unsigned char stat;
490 //struct net_local *lp=(struct net_local *)dev->priv;
491 spin_lock_irq(&lplock);//互斥锁
492 netif_stop_queue(dev); //停止网络发送
493 #ifdef DEBUG
494 PRINTK("EN28J60 send!!/n");
495 #endif
496 /* Disable all interrupt */
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 // Set the TXND pointer to correspond to the packet size given
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("send erro!/n");
509 spin_unlock_irq(&lplock);
510 return 1;
511 }
512 // write per-packet control byte lht
513 enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); //use MCON3 参考42页
514 // copy the packet into the transmit buffer
515 enc28j60WriteBuffer(skb->len, skb->data);
516 //udelay(100);
517 // send the contents of the transmit buffer onto the network
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 return 0;
527 }
528
529
530 /*****************************************************************************/
531 static int enc28j60_send (void)
532 {
533 unsigned char stat,i;
534 char buff[70];
535 unsigned char tx_length=69;
536 for(i=0;i<70;i++)
537 buff[i]=i;
538 spin_lock_irq(&lplock);//互斥锁
539 PRINTK("EN28J60 send!!/n");
540 /* Disable all interrupt */
541 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIE, EIE_INTIE);
542 // mc=enc28j60Read(MAADR5);
543 //printk("mac5=%x/n",mc);
544 // Set the write pointer to start of transmit buffer area
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 // Set the TXND pointer to correspond to the packet size given
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("send erro!/n");
558 spin_unlock_irq(&lplock);
559 return 1;
560 }
561 // write per-packet control byte lht
562 enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x06); //use MCON3 参考42页
563 // copy the packet into the transmit buffer
564 enc28j60WriteBuffer(tx_length, buff);
565 //udelay(100);
566 // send the contents of the transmit buffer onto the network
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 return 0;
574 }
上面代码与裸机驱动差异只在struct sk_buff上面,所以实现起来也就较为简单了。
最后,接收的代码就出现在中断处理函数中了,收到数据需要重新申请struct sk_buff,然后根据接收的内容进行相应的填充,最后由netif_rx(skb);提交给网络层。
577 static irqreturn_t enc28j60_Interrupt(int irq, void *dev_id)
578 {
579 struct net_device *dev = dev_id;
580 struct sk_buff *skb;
581 u16 rxstat;
582 u16 len=0;
583 u16 status,PKCNT;
584 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIE, EIE_INTIE); //disable irq
585 status=enc28j60Read(EIR);
586 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIR, status);
587 if(status & EIR_TXIF) //发送中断
588 {
589 #ifdef DEBUG
590 PRINTK("tx packet ok!!/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 ad the packet length
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 // copy the packet from the receive buffer
617 enc28j60ReadBuffer(len, skb->data);
618 // Move the RX read pointer to the start of the next received packet
619 // This frees the memory we just read out
620 #ifdef DEBUG
621 PRINTK("rx sucess!/n")
622 PRINTK("length=%d/n",len);
623 u16 i;
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 // decrement the packet counter indicate we are done with this packet
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("TX erro/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("RX erro/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 return IRQ_HANDLED;
658 }
以上就是一个网络设备驱动几个比较关键的环节了,最后的工作就是简单修改makefile和kconfig的内容使其支持该驱动即可。以模块形式加载以后如果无异常,在proc/device中将出现设备的名字。通过ifconfig+设备名+ip。将激活网络设备。最后注意两张网卡不能处于同一网段,否则无法工作。一切完了之后,就可以用PING命令来测试网络连接了。
其实从上面的实现过程中我们不难发现,网络设备驱动与裸机驱动最大的区别就是需要填充几个网络层的接口函数,并且要通过相应的注册函数将内核指定的数据结构注册进内核。这一过程恰恰和字符设备的几个过程惊人的相似,所以参考最熟悉的字符设备驱动编写的几个流程,来编写网络设备驱动就异常清晰了。