DM9000的驱动为:
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/delay.h> #include <linux/string.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #define DM9K_MAJOR 244 /*预设DM9000的主设备号*/ #define MEM_BUF_SIZE 512 //内核空间存放从用户空间拷贝过来数据区大小 #define SET1(X, Y) (X)|=(1<<(Y)) #define SET0(X, Y) (X)&=(~(1<<(Y))) //内核数据空间 char membuf[MEM_BUF_SIZE]; //IPID static unsigned short ipid=0; //本机IP char myip[4]={192,168,0,88}; //本机MAC char mymac[6]={0xff,0xff,0xff,0xff,0xff,0xff}; //目的IP char targetip[4]={192,168,0,54}; char targetmac[6]={0x00,0x21,0x97,0xc4,0x9d,0xae}; //DM9000网卡的地址端口 char* io_addr=NULL; //DM9000网卡的数据端口 char* io_data=NULL; struct cdev* dm9k_cdev=NULL; //CRC校验 int chksum16(void *buf1, short len, int chksum, char complement) { unsigned short * buf = buf1; int chksum16; while (len > 0) { chksum16 = (len == 1) ? ((*buf)&0x00FF) : (*buf); chksum = chksum + htons(chksum16); *buf++; len -=2; } if (complement) return (~(chksum + ((chksum & 0xFFFF0000) >> 16))&0xFFFF); return chksum; } //寄存器读写函数 static u8 ior(u8 reg) { writeb(reg, io_addr); return readb(io_data); } static void iow(u8 reg, u8 value) { writeb(reg, io_addr); writeb(value, io_data); } //UDP头部 struct udp_hdr { unsigned short src_port; // 源端口 unsigned short dest_port; // 目的端口 unsigned short len; // 总长度(UPD头部长度 + data长度) unsigned short chksum; // UDP校验和,可以为0 }__attribute__ ((packed)); //IP头部copy_data struct ip_hdr { char ver_hl; // 版本、头部长度 – 固定0x45 char tos; // 服务类型 – 固定0x00 unsigned short len; // 总长度(IP头 + UDP头 + 正文) unsigned short id; // 报文编号(每个报文必须不同) unsigned short fragment; // 分段信息 – 固定0x4000 char ttl; // 生存期 – 固定64 char protocol; // 协议 – 固定17 unsigned short hdr_chksum; // IP头部校验和(不包括正文) char src_ip [4]; // 源IP char dest_ip [4]; // 目标IP }__attribute__ ((packed)); //MAC头部 struct ethernet_hdr { char dest_mac[6]; /* 目的MAC */ char src_mac[6]; /* 源MAC */ short protocol; /* 协议 – 固定0x0800 */ }__attribute__ ((packed)); #define FRAME_BUF_SIZE 1024 char frame[FRAME_BUF_SIZE]; //设定报文的最大长度为1024字节 #define IP_START 14 //IP头部的开始位置偏移 #define UDP_START (14+20) //UPD头部的开始位置偏移 #define DATA_START (14+20+8) //数据开始位置偏移 struct ethernet_hdr* ethhdr=(struct ethernet_hdr*)frame; struct ip_hdr* iphdr=(struct ip_hdr*)(frame+IP_START); struct udp_hdr* udphdr=(struct udp_hdr*)(frame+UDP_START); //封装UDP头部 void pack_udp_hdr(unsigned short datalen) { udphdr->src_port=htons((short)0x3000);//把源端口号定死 udphdr->dest_port=htons((short)0x3000);//把目的端口号也定死 udphdr->len=htons(datalen+8); udphdr->chksum=0; } //封装IP头部 void pack_ip_hdr(unsigned short datalen) { iphdr->ver_hl=0x45; iphdr->tos=0x00; iphdr->len=htons(datalen+sizeof(struct udp_hdr)+sizeof(struct ip_hdr)); ipid++; iphdr->id=htons(ipid); iphdr->fragment=htons(0x4000); iphdr->ttl=64; iphdr->protocol=17; memcpy(iphdr->dest_ip, targetip, 4); memcpy(iphdr->src_ip, myip, 4); iphdr->hdr_chksum=0;//这步非常重要 iphdr->hdr_chksum=htons(chksum16(iphdr, sizeof(struct ip_hdr), 0, 1)); } //封装MAC头部 void pack_mac_hdr(void) { memcpy(ethhdr->dest_mac, targetmac, 6 ); memcpy(ethhdr->src_mac, mymac, 6); ethhdr->protocol=htons((short)0x0800); } //填充数据 void copy_data(unsigned int len) { memcpy(frame+DATA_START, membuf, len); frame[DATA_START+len]='/0'; } //额外的初始化 #define WAKEST (1<<5) #define TX2END (1<<3) #define TX1END (1<<2) #define ROO (1<<3) #define ROS (1<<2) #define PT (1<<1) #define PR (1<<0) #define DIS_LONG (1<<5) #define DIS_CRC (1<<4) #define RXEN (1<<0) #define PAR (1<<7) #define PTI (1<<1) #define PRI (1<<0) #define LNKCHGI (1<<5) static int dm9k_open(struct inode* inode, struct file* filp) { //清除几个寄存器 //TCR = 0, BPTR = 0x3f, FCR = 0xff, SMCR = 0 iow(0x2, 0x0); iow(0x8, 0x3f); iow(0x0a, 0xff); iow(0x2f, 0x0); //清除发送状态 //01H NSR(网络状态寄存器) = WAKEST | TX2END | TX1END //FEH ISR(输入状态 寄存器)= ROO | ROS | PT | PR //05H RCR(接收控制寄存器) = DIS_LONG|DIS_CRC|RXEN //FFH IMR(中断mask寄存器) = PAR | PTI | PRI | LNKCHGI iow(0x01, WAKEST|TX2END|TX1END); iow(0xfe, ROO|ROS|PT|PR); iow(0x05, DIS_LONG|DIS_CRC|RXEN); iow(0xff, PAR|PTI|PRI|LNKCHGI); return 0; } static ssize_t dm9k_write(struct file* filp, const char __user* buf, size_t size, loff_t* ppos) { int i=0; unsigned short totallen=DATA_START+size;//报文总长度 if( copy_from_user(membuf, buf, size)<0 ) { printk("copy_from_user error!/n"); return -1; } pack_udp_hdr(size); pack_ip_hdr(size); pack_mac_hdr(); copy_data(size); printk(KERN_ALERT "total=%d/n", totallen); //将报文写入网卡的数据缓冲区 //向io_addr写入0xF8,代表寄存器MWCMD //(带有地址增量寄存器的内存数据写命令) //对报文做一次遍历:向io_data每次写入2个字节( 16 bits mode ) , //直到整个报文发送完毕 writeb(0xf8, io_addr); //FRAME_BUF_SIZE一般为偶数 for( i=0 ; i<totallen; i += 2 ) { writew( *(unsigned short*)(frame+i), io_data); //printk(KERN_ALERT "%c %c ", frame[i], frame[i + 1]); } //将报文长度写入网卡 //向(FCH TXPLL, FDH TXPLH) “TX包长度寄存器”写入报文的长度, //其中TXPLH写入高字节, TXPLL写入低字节 iow(0xfd, totallen >> 8); iow(0xfc, totallen & 0xff); //通知网卡开始发送报文 //向02H TCR(TX控制寄存器)写入1 iow(0x2, 0x1); printk(KERN_ALERT "write done./n"); return totallen; } struct file_operations dm9k_fops={ owner: THIS_MODULE, open: dm9k_open, //read: dm9k_read, write: dm9k_write }; static int __init dm9k_init(void) { int i=0; int result; dev_t devno=MKDEV(DM9K_MAJOR, 0); result=register_chrdev_region(devno, 1, "dm9k"); if( result<0 ) { printk("init error!/n"); return result; } //地址端口:0x18000300,8位 //数据端口:0x18000304, 8位 io_addr=ioremap(0x18000300, 0x1); io_data=ioremap(0x18000304, 0x1); dm9k_cdev=cdev_alloc(); cdev_init(dm9k_cdev, &dm9k_fops); dm9k_cdev->owner=THIS_MODULE; dm9k_cdev->ops=&dm9k_fops; result=cdev_add(dm9k_cdev, devno, 1); if( result<0 ) { printk("cdev_add Error/n"); return result; } //网卡的初始化 //激活内部物理层 //设置 GPCR (1EH) bit[0]=1,使DM9000的GPIO3为输出 //GPR (1FH) bit[0]=0 ,使GPIO3输出为低以激活内部PHY //延时2ms以上,等待PHY上电 writeb(0x1E, io_addr); SET1(*io_data, 0); writeb(0x1F, io_addr); SET0(*io_data, 0); mdelay(10); //软件复位 //NCR=0x03,软件复位, 0x00H //延时20us以上等待软件复位完成 //NCR=0x00,复位完成,设置正常工作模式 //NCR=0x03,第二次软件复位,为了确保软件复位完全成功 //延时20us //NCR=0x00 iow(0x0, 0x03); mdelay(10); iow(0x0, 0x0); iow(0x0, 0x03); mdelay(10); iow(0x0, 0x0); //清除标志位 //NSR = 0x2c,清除各种状态标志位,0x01H //ISR = 0x3f,清除所有中断标志位,0xFEH iow(0x01, 0x2c); iow(0xFE, 0x3f); //IMR=0x81,中断使能,FFH iow(0xFF, 0x81); //读取网卡的产品ID(PIDH, PIDL)、生产商ID(VIDH, VIDL),共4个字节 //判断两个ID是否符合正确的默认值 //读取网卡的MAC地址,保存备用 //2Ah PIDL,产品ID的低位字节,默认值00H //2Bh PIDH,产品ID的高位字节,默认值90H printk("PID=0x%02x%02x/n", ior(0x2B), ior(0x2A)); //printk("PIDL=%x/n", ior(0x2A)); printk("VID=0x%02x%02x/n", ior(0x29), ior(0x28)); //printk("VIDL=%x/n", ior(0x28)); //读取MAC地址 //物理地址寄存器(PHR):用于读出网卡MAC地址 //从10H开始,连续6个字节 printk("MAC:"); for( i=0 ; i<6 ; i++ ) { if( i!=5 ) { printk("%02x:", ior(0x10+i)); } else { printk("%02x", ior(0x10+i)); } } printk("/n"); printk("init success!/n"); return 0; } static void __exit dm9k_exit(void) { iounmap(io_addr); iounmap(io_data); cdev_del(dm9k_cdev); unregister_chrdev_region(MKDEV(DM9K_MAJOR, 0), 1); printk("exit/n"); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("Peng Li"); module_init(dm9k_init); module_exit(dm9k_exit);
这个驱动最容易出错的地方就在ip头部的校验和的计算,一定要先把它设置为0再计算,否则ip校验和总是计算错误。
发送端程序
需要先执行mknod /dev/dm9k c 244 0
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #define DEVICE_DM9K "/dev/dm9k" int main(int argc, char *argv[]) { int fd; if( argc!=2 ) { printf("arguments error!"); exit(1); } fd=open(DEVICE_DM9K, O_RDWR); if( fd<0 ) { perror("cannot open device!"); exit(1); } write(fd, argv[1], strlen(argv[1])); close(fd); return 0; }
监听程序我就不写了,也可以通过wireshark抓包查看结果。