对技术执着
分类: LINUX
2015-03-14 15:20:50
原文地址:Linux下DM9000网卡驱动实验〈三〉 作者:newcch
驱动程序共包含三个文件dm9000x.c ,dm9000.c 和dm9000.h,都存放在drivers/net/目录下,其中dm9000x.c 主要包括以下函数:
底层硬件操作函数:这些函数与硬件相关,与驱动程序编写相关不大。
void outb(unsigned char value, unsigned long addr) void outw(unsigned short value, unsigned long addr) unsigned char inb(unsigned long addr) unsigned short inw(unsigned long addr)
u8 ior(board_info_t *db, int reg) // Read a byte from I/O port void iow(board_info_t *db, int reg, u8 value) // Write a byte to I/O port
|
驱动程序的函数接口,
int init_module(void) ; //加载设备驱动程序 void cleanup_module(void) //卸载设备驱动程序
static void dmfe_init_dm9000(struct DEVICE *dev) /* Initilize DM910X board */ dmfe_probe(struct DEVICE *dev); //初始化net_device 结构体 static int dmfe_open(struct DEVICE *dev) static int dmfe_stop(struct DEVICE *dev) static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) //发送一个数据包 static void dmfe_packet_receive(struct DEVICE *dev, board_info_t *db) //接收数据包,被中断调用
|
其中int init_module(void)函数调用了dmfe_probe(struct DEVICE *dev)来初始化net_device结构体
if(strcmp(dev->name,"eth0")==0) if(strcmp(dev->name,"eth1")==0) if(strcmp(dev->name,"eth3")==0)
outb(DM9000_VID_L, iobase); id_val = inb(iobase + 4); outb(DM9000_VID_H, iobase); id_val |= inb(iobase + 4) << 8; outb(DM9000_PID_L, iobase); id_val |= inb(iobase + 4) << 16; outb(DM9000_PID_H, iobase); id_val |= inb(iobase + 4) << 24;
if (id_val == DM9000_ID) { printk("HHTech DM9000 %s I/O: %x,VID: %x,MAC: ", dev->name,iobase, id_val); dm9000_count++; /* Init network device */ dev = init_etherdev(dev, 0);
/* Allocated board information structure */ irqline = 3; db = (void *)(kmalloc(sizeof(*db), GFP_KERNEL|GFP_DMA)); memset(db, 0, sizeof(*db)); dev->priv = db; /* link device and board info */ db->next_dev = dmfe_root_dev; dmfe_root_dev = dev; db->ioaddr = iobase; db->io_data = iobase + 4;
/* driver system function */ dev->base_addr = iobase; //dev->irq = 68; //INT4 166;//irqline; dev->open = &dmfe_open; dev->hard_start_xmit = &dmfe_start_xmit; dev->stop = &dmfe_stop; dev->get_stats = &dmfe_get_stats; dev->set_multicast_list = &dm9000_hash_table; dev->do_ioctl = &dmfe_do_ioctl;
|
在dmfe_open(struct DEVICE *dev)中,申请了中断处理程序,然后调用了
dmfe_init_dm9000(dev);初始化DM9000芯片。
static int dmfe_open(struct DEVICE *dev) { if(request_irq(dev->irq, &dmfe_interrupt,SA_INTERRUPT/*SA_SHIRQ*/, "DM9000 device",dev)) return -EAGAIN; /* Initilize DM910X board */ dmfe_init_dm9000(dev);
netif_wake_queue(dev); //add by simon 2001.9.4 for kernel 2.4 MOD_INC_USE_COUNT; } |
Stop()方法停止网络驱动程序:
static int dmfe_stop(struct DEVICE *dev) { netif_stop_queue(dev); //add by simon 2001.9.4 for kernel 2.4
/* free interrupt */ free_irq(dev->irq, dev);
/* RESET devie */ phy_write(db, 0x00, 0x8000); /* PHY RESET */ iow(db, 0x iow(db, 0xff, 0x80); /* Disable all interrupt */ iow(db, 0x05, 0x00); /* Disable RX */
MOD_DEC_USE_COUNT; return 0; } |
发送数据接口,对照前面的简单的驱动框架。
static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) { netif_stop_queue(dev); //add by simon 2001.9.4 for kernel 2.4
/* Disable all interrupt */ iow(db, 0xff, 0x80);
/* Move data to DM9000 TX RAM */ data_ptr = (char *)skb->data; outb(0xf8, db->ioaddr); tmplen = (skb->len + 1) / 2; /* Word mode*/ for (i = 0; i < tmplen; i++) outw(((u16 *)data_ptr)[i], db->io_data);
/* TX control: First packet immediately send, second packet queue */ if (db->tx_pkt_cnt == 0) { /* First Packet */ db->tx_pkt_cnt++;
/* iow(db, 0xfc, skb->len & 0xff); iow(db, 0xfd, (skb->len >> 8) & 0xff);
/* iow(db, 0x2, 0x1); /* Cleared after TX complete */
dev->trans_start = jiffies; /* saved the time stamp */ } else { /* Second packet */ db->tx_pkt_cnt++; db->queue_pkt_len = skb->len; }
/* free this SKB */ dev_kfree_skb(skb);
/* Re-enable resource check */ if (db->tx_pkt_cnt == 1) netif_wake_queue(dev); //add by simon 2001.9.4 for kernel 2.4
/* Re-enable interrupt mask */ iow(db, 0xff, 0x83);
return 0; } |
中断处理程序,用来接收数据
static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs) { db = (board_info_t *)dev->priv; spin_lock(&db->lock); // add by simon 2001.9.4 for kernel 2.4
/* Save previous register address */ reg_save = inb(db->ioaddr);
/* Disable all interrupt */ iow(db, 0xff, 0x80);
/* Got DM9000 interrupt status */ int_status = ior(db, 0xfe); /* Got ISR */ iow(db, 0xfe, int_status); /* Clear ISR status */ //printk("I%x ", int_status);
/* Received the coming packet */ if (int_status & 1) dmfe_packet_receive(dev, db);
/* Trnasmit Interrupt check */ if (int_status & 2) { tx_status = ior(db, 0x01); /* if (tx_status & 0xc) { /* One packet sent complete */ db->tx_pkt_cnt--; dev->trans_start = 0; db->stats.tx_packets++;
/* Queue packet check & send */ if (db->tx_pkt_cnt > 0) { iow(db, 0xfc, db->queue_pkt_len & 0xff); iow(db, 0xfd, (db->queue_pkt_len >> 8) & 0xff); iow(db, 0x2, 0x1); dev->trans_start = jiffies; }
//dev->tbusy = 0; /* Active upper layer, send again */ //mark above by simon 2001.9.4 for kernel 2.4 netif_wake_queue(dev); //mark_bh(NET_BH); //mark by simon 2001.9.4 } }
/* Re-enable interrupt mask */ iow(db, 0xff, 0x83);
/* Restore previous register address */ outb(reg_save, db->ioaddr); |
在内核的DM9000驱动代码中,直接增加调试和打印信息,然后重新编译内核,下载到开发板上,察看调试信息。内核中的打印输出语句用printk()。
把内核中的DM9000的网络驱动程序/drivers/net/dm9000x.c中的注释掉的printk语句打开。详细见dm9000x.c文件。
第一步,打印网络的配置信息,与ifconfig显示的信息比较。
1, 在/kernel/drivers/net/dm9000x.c中,注释掉的printk有效。主要是在dmfe_probe(struct DEVICE *dev)函数中的打印信息.
2, 然后再/kernel目录下编译内核,make zImage
3, 通过TFTP下载刚编译的内核镜像zImage到开发板中
tftp 30008000 zImage tftp 30800000 ramdisk.image.gz go 30008000
|
4,观察系统启动时有网络驱动程序打印出来的信息。进入系统后用ifconfig察看网络信息。
第二步:观察中断调用,数据接收的情况
1, 在/kernel/drivers/net/dm9000x.c中,使宏定义有效
//#undef DM9000_DEBUG
#define DM9000_DEBUG
主要是在
static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void dmfe_packet_receive(struct DEVICE *dev, board_info_t *db)
中的打印信息, 注释掉的printk有效.
2, 然后再/kernel目录下编译内核,make zImage
3, 通过TFTP下载刚编译的内核镜像zImage到开发板中
tftp 30008000 zImage
tftp 30800000 ramdisk.image.gz
go 30008000
4,观察系统启动时有网络驱动程序打印出来的信息。进入系统后挂载NFS系统(如果还没有挂载NFS),可以看到打印出网络中断和数据接收的信息。
第二步:用户层调用网络socket接口。
1, 因为操作系统封装了网络设备驱动程序,用户层只能通过socket接口使用网络。 HHARM2410-STUDY\modules.TestApp\ethernet-performance-test提供了测试网络流量的测试程序,使用了socket接口。
2, 分别编译HHARM2410-STUDY\modules.TestApp\ethernet-performance-test\run-on-board和HHARM2410-STUDY\modules.TestApp\ethernet-performance-test\run-on-LINUX-PC
3,开发板用网线(对接线)同PC机相连;
PC机上编译好eth-perf-pc/中的程序,生成server可执行程序,运行
./server
在minicom下执行客户端程序(开发板上)
./client 192.168.2.111 –t 1 –p 1
其中192.168.2.111为PC机的IP,-t表示time,-p表示package。
4,如果这时候没有注释掉网络驱动程序dm9000的中断和接收数据的打印信息,屏幕会频繁的提示产生中断和接收到数据。
还可以用
[localhost]# cat /prop/interrupts
察看中断使用情况,可以看到网络驱动程序使用中断号,和使用次数。