一.tftp作用:在嵌入式开发中进行数据传输
6410完整代码
二.交互过程:
1.客户端发送请求包(请求报文)
2.根据选项参数,服务器决定是否发送应答确认包,进而客户端发送确认接收包,这步是可选的。
3.服务器发送数据包1,。服务器会将要传输的数据拆分成许多小块,每512字节一个块。(数据报文)
4.客户端回应数据包1(应答报文)
5.服务器如果没有收到数据包1回应包,就会重复发送数据包1直到超时.收到了就继续发送数据包2.
6.客户端回应数据包2.
7.以此类推,服务器发送数据包n,客户端回应数据包n。
8.。。。。。。。。。。。。。
三.报文格式:
1.tftp是属于UDP协议的,UDP又是属于IP协议的。IP又是属于以太网的。所以客户端必须知道目的端的mac地址,所以之前要发送一个ARP包。
2.根据操作码来区分不同的tftp报文。
3.操作码 = 1:表示客户端要从服务器下载数据,属于请求读。后面跟上要读些的文件名,再追加一个0.再后面是读取的模式,分为ASCiI码和普通的八位二进制数格式,末尾追加一个0.
4.操作码 = 2:表示请求从客户端上传数据到服务器。 后面格式和读取(下载)一样。
5.操作码 = 3:表示数据报文,后面紧跟块编号,末尾就是真正的数据内容
6.操作码 = 4:表示应答报文,后面以他应答的数据块编号结尾,
7.操作码 = 5:表示出错。后面紧跟差错码和差错信息。
四.修改网卡驱动
1.在arp.c增加网络处理函数。判断是什么类型的包。之前的代码默认是一个ARP包,但实际情况很复杂,所以要判断网卡接收到的是什么包。
2.在dm9000.c的中断处理里面对参数进行修改。包括buffer,以及调用通用处理函数,而不是简单地调用ARP应答解析函数。
五.IP协议格式
相应的数据结构为
/*IP头部结构体*/
typedef struct ip_hdr
{
ETH_HDR ethhdr;
u8 vhl;
u8 tos;
u16 len;
u16 ipid;
u16 ipoffset;
u8 ttl;
u8 proto;
u16 ipchksum;
u8 srcipaddr[4];
u8 destipaddr[4];
}IP_HDR;
六.UDP协议格式
数据结构
/*UDP头部结构体*/
typedef struct udp_hdr
{
IP_HDR iphdr;
u16 sport;
u16 dport;
u16 len;
u16 udpchksum;
}UDP_HDR;
七。关于DM9000打开接收广播功能的datasheet部分
void
dm9000_active()
{
/* RX enable */
dm9000_write(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);//要加上RCR_ALL,否则他会自动丢弃来自外部的广播包
/* Enable TX/RX interrupt mask */
dm9000_write(DM9000_IMR, IMR_PAR);
}
八.处理触发中断的网络包
/*对来自外部的包进行处理*/
void net_handle(u8* buf,u32 len)
{
ETH_HDR* p = (ETH_HDR*)buf;
//printf("dm_handle int%x\r\n",SWAP(p->frame_type));
switch(SWAP(p->frame_type)){
case PROTO_ARP:
arp_respond(buf,len); //ARP协议应答,目的是获取PC的MAC地址
break;
case PROTO_IP:
ip_respond(buf,len); //对数据传送的包进行处理
break;
default:
break;
}
return;
}
九.对IP协议的包以及从属于IP协议的UDP协议的包进行处理
extern void tftp_process(u8* buf,u32 len,u16 port);
/*对UDP协议的包,进行处理。TFTP传输是属于UDP协议的,而UDP又是属于IP协议的*/
void udp_respond(u8* buf,u32 len)
{
}
/*对IP协议的包进行处理,因为可能涉及到IP协议的情况不只是TFTP传输,所以为以后程序功能升级做准备,就单独写一个判断函数*/
void ip_respond(u8* buf,u32 len)
{
IP_HDR* iphdr = (IP_HDR*)buf;
//printf("dm_ip int\r\n");
if(iphdr->proto == PROTO_UDP)
udp_respond(buf,len);
}
十.TFTP发送请求信号,准备读取服务器的数据
/*客户端发送读取服务器数据请求*/
void tftp_request(const char* filename)
{
u8* p_tftp = &sendbuf[200]; //tftp头部开始地址
u32 tftp_index = 0; //作为下标表示位置的移动
UDP_HDR* udphdr;
u8* iphdr;
/*1.构建tftp头部*/
p_tftp[0] = 0x00; //网络字节序(大端模式),写入操作码
p_tftp[1] = 0x01;
tftp_index += 2; //下标移动
sprintf(&p_tftp[tftp_index],"%s",filename); //写入文件名
tftp_index += strlen(filename);
p_tftp[tftp_index] = '\0'; //文件名尾部追加0
tftp_index += 1;
sprintf(&p_tftp[tftp_index],"%s","octet"); //写入数据传输模式
tftp_index += strlen("octet");
p_tftp[tftp_index] = '\0'; //尾部追加0
tftp_index += 1;
/*2.构建udp头部*/
udphdr = (UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
iphdr = (u8*)udphdr + sizeof(ETH_HDR);
udphdr->sport = SWAP(48915); //参数默认选这些
udphdr->dport = SWAP(69);
udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
udphdr->udpchksum = 0x00;
/*3.构建ip头部*/
udphdr->iphdr.vhl = 0x45; //固定值
udphdr->iphdr.tos = 0x00; //固定值
udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
udphdr->iphdr.ipid = SWAP(0x00); //固定值
udphdr->iphdr.ipoffset = SWAP(0x4000); //固定值
udphdr->iphdr.ttl = 0xff; //固定值
udphdr->iphdr.proto = 0x11; //固定值
memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);
memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
udphdr->iphdr.ipchksum = 0;
udphdr->iphdr.ipchksum = checksum(iphdr,20); //ip头部长度固定为20个字节
memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}
十一.客户端接收到来自服务器端的数据包的应答函数
/*客户端响应服务器已正常收到来自服务器的数据包*/
void tftp_send_ack( u16 blocknum,u16 serverport)
{
u8* p_tftp = &sendbuf[200]; //tftp头部开始地址
u32 tftp_index = 0; //作为下标表示位置的移动
UDP_HDR* udphdr;
u8* iphdr;
/*1.构建tftp头部*/
p_tftp[0] = 0x00; //网络字节序(大端模式),写入操作码
p_tftp[1] = 0x04;
tftp_index += 2; //下标移动
p_tftp[2] = (blocknum & 0xff00) >> 8; //网络字节序(大端模式),写入块编号,要反转高低位
p_tftp[3] = blocknum & 0xff;
tftp_index += 2; //下标移动
/*2.构建udp头部*/
udphdr =(UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
iphdr = (u8*)udphdr + sizeof(ETH_HDR);
udphdr->sport = SWAP(48915); //参数默认选这些
udphdr->dport = serverport;//这是目的端口号,即服务器端口号,可以从来自服务器的数据包获取
udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
udphdr->udpchksum = 0x00;
/*3.构建ip头部*/
udphdr->iphdr.vhl = 0x45; //固定值
udphdr->iphdr.tos = 0x00; //固定值
udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
udphdr->iphdr.ipid = SWAP(0x00); //固定值
udphdr->iphdr.ipoffset = SWAP(0x4000); //固定值
udphdr->iphdr.ttl = 0xff; //固定值
udphdr->iphdr.proto = 0x11; //固定值
memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);
memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
udphdr->iphdr.ipchksum = 0;
udphdr->iphdr.ipchksum = checksum(iphdr,20); //ip头部长度固定为20个字节
memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}
十二.TFTP客户端处理函数
/*TFTP对数据传输的处理*/
void tftp_process(u8* buf,u32 len,u16 port)
{
u32 i = 0,length =0;
u8* tftp_download_addr = (u8*)TFTP_START_ADDR;
TFTP_PAK* p_pkg = (TFTP_PAK*)(buf + sizeof(UDP_HDR));
length = len - sizeof(UDP_HDR) - 4;
/*判断接收到的包是否是数据包*/
if(SWAP(p_pkg->opcode) == 3){
/*判断接收到的包是否是最后一个*/
if(SWAP(p_pkg->blocknum) == cur_blocknum){
tftp_download_addr = (u8*)(TFTP_START_ADDR + (cur_blocknum - 1)*512);
/*复制数据到指定内存地址*/
for(i = 0;i < length;i++){
*(tftp_download_addr++) = *(p_pkg->data + i);
}
/*客户端发送接收到数据包的应答信号*/
tftp_send_ack(SWAP(p_pkg->blocknum),port);
cur_blocknum++;//更新标志块编号
printf("tftp: %d\r\n",cur_blocknum);
/*如果是最后一个数据包,则复位标志块编号和内存起始地址,为下一次传输做准备*/
if(length < 512){
cur_blocknum = 1;
tftp_download_addr = (u8*)TFTP_START_ADDR;
printf("Download Completely!\n\r");
}
}
}
}
6410完整代码
/****************************
@File:tftp.c
@
@Tiny6410裸机下学期代码
@tftp协议配置文件
@Author:小君君
@****************************/
#include "common.h"
#include "string.h"
#include "arp.h"
u8 sendbuf[1024];
u32 cur_blocknum = 1;
#define TFTP_START_ADDR 0x51000000
/*对IP包进行校检*/
u16 checksum(u8 *ptr, int len)
{
u32 sum = 0;
u16 *p = (u16 *)ptr;
while (len > 1)
{
sum += *p++;
len -= 2;
}
if(len == 1)
sum += *(u8 *)p;
while(sum>>16)
sum = (sum&0xffff) + (sum>>16);
return (u16)((~sum)&0xffff);
}
/*客户端发送读取服务器数据请求*/
void tftp_request(const char* filename)
{
u8* p_tftp = &sendbuf[200]; //tftp头部开始地址
u32 tftp_index = 0; //作为下标表示位置的移动
UDP_HDR* udphdr;
u8* iphdr;
/*1.构建tftp头部*/
p_tftp[0] = 0x00; //网络字节序(大端模式),写入操作码
p_tftp[1] = 0x01;
tftp_index += 2; //下标移动
sprintf(&p_tftp[tftp_index],"%s",filename); //写入文件名
tftp_index += strlen(filename);
p_tftp[tftp_index] = '\0'; //文件名尾部追加0
tftp_index += 1;
sprintf(&p_tftp[tftp_index],"%s","octet"); //写入数据传输模式
tftp_index += strlen("octet");
p_tftp[tftp_index] = '\0'; //尾部追加0
tftp_index += 1;
/*2.构建udp头部*/
udphdr = (UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
iphdr = (u8*)udphdr + sizeof(ETH_HDR);
udphdr->sport = SWAP(48915); //参数默认选这些
udphdr->dport = SWAP(69);
udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
udphdr->udpchksum = 0x00;
/*3.构建ip头部*/
udphdr->iphdr.vhl = 0x45; //固定值
udphdr->iphdr.tos = 0x00; //固定值
udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
udphdr->iphdr.ipid = SWAP(0x00); //固定值
udphdr->iphdr.ipoffset = SWAP(0x4000); //固定值
udphdr->iphdr.ttl = 0xff; //固定值
udphdr->iphdr.proto = 0x11; //固定值
memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);
memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
udphdr->iphdr.ipchksum = 0;
udphdr->iphdr.ipchksum = checksum(iphdr,20); //ip头部长度固定为20个字节
memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}
/*客户端响应服务器已正常收到来自服务器的数据包*/
void tftp_send_ack( u16 blocknum,u16 serverport)
{
u8* p_tftp = &sendbuf[200]; //tftp头部开始地址
u32 tftp_index = 0; //作为下标表示位置的移动
UDP_HDR* udphdr;
u8* iphdr;
/*1.构建tftp头部*/
p_tftp[0] = 0x00; //网络字节序(大端模式),写入操作码
p_tftp[1] = 0x04;
tftp_index += 2; //下标移动
p_tftp[2] = (blocknum & 0xff00) >> 8; //网络字节序(大端模式),写入块编号,要反转高低位
p_tftp[3] = blocknum & 0xff;
tftp_index += 2; //下标移动
/*2.构建udp头部*/
udphdr =(UDP_HDR*)(p_tftp - sizeof(UDP_HDR));
iphdr = (u8*)udphdr + sizeof(ETH_HDR);
udphdr->sport = SWAP(48915); //参数默认选这些
udphdr->dport = serverport;//这是目的端口号,即服务器端口号,可以从来自服务器的数据包获取
udphdr->len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(IP_HDR));
udphdr->udpchksum = 0x00;
/*3.构建ip头部*/
udphdr->iphdr.vhl = 0x45; //固定值
udphdr->iphdr.tos = 0x00; //固定值
udphdr->iphdr.len = SWAP(tftp_index+sizeof(UDP_HDR)-sizeof(ETH_HDR));
udphdr->iphdr.ipid = SWAP(0x00); //固定值
udphdr->iphdr.ipoffset = SWAP(0x4000); //固定值
udphdr->iphdr.ttl = 0xff; //固定值
udphdr->iphdr.proto = 0x11; //固定值
memcpy(udphdr->iphdr.srcipaddr,ip_addr,4);
memcpy(udphdr->iphdr.destipaddr,host_ip_addr,4);
udphdr->iphdr.ipchksum = 0;
udphdr->iphdr.ipchksum = checksum(iphdr,20); //ip头部长度固定为20个字节
memcpy(udphdr->iphdr.ethhdr.s_mac,mac_addr,6);
memcpy(udphdr->iphdr.ethhdr.d_mac,host_mac_addr,6);
udphdr->iphdr.ethhdr.frame_type = SWAP(PROTO_IP);
dm9000_tx((u8 *)udphdr,sizeof(UDP_HDR)+tftp_index);
}
/*TFTP对数据传输的处理*/
void tftp_process(u8* buf,u32 len,u16 port)
{
u32 i = 0,length =0;
u8* tftp_download_addr = (u8*)TFTP_START_ADDR;
TFTP_PAK* p_pkg = (TFTP_PAK*)(buf + sizeof(UDP_HDR));
length = len - sizeof(UDP_HDR) - 4;
/*判断接收到的包是否是数据包*/
if(SWAP(p_pkg->opcode) == 3){
/*判断接收到的包是否是最后一个*/
if(SWAP(p_pkg->blocknum) == cur_blocknum){
tftp_download_addr = (u8*)(TFTP_START_ADDR + (cur_blocknum - 1)*512);
/*复制数据到指定内存地址*/
for(i = 0;i < length;i++){
*(tftp_download_addr++) = *(p_pkg->data + i);
}
/*客户端发送接收到数据包的应答信号*/
tftp_send_ack(SWAP(p_pkg->blocknum),port);
cur_blocknum++;//更新标志块编号
printf("tftp: %d\r\n",cur_blocknum);
/*如果是最后一个数据包,则复位标志块编号和内存起始地址,为下一次传输做准备*/
if(length < 512){
cur_blocknum = 1;
tftp_download_addr = (u8*)TFTP_START_ADDR;
printf("Download Completely!\n\r");
}
}
}
}
/****************************
@File:main.c
@
@Tiny6410裸机下学期代码
@网卡测试文件
@Author:小君君
@****************************/
#include "common.h"
int main(void)
{
int num = 1000;
led_init();//LED的GPIO初始化
button_init();//按键初始化
led_on();//点亮4颗LED
uart_init();//串口初始化
putchar('a');
putchar('\r');
putchar('\n');
putchar('b');
putchar('\r');
putchar('\n');
dma_init();//DMA初始化
dma_start();//启动DMA发送数据到串口
uart_init();//串口再次初始化,使得串口恢复中断或者轮询模式
lcd_init();
lcd_clear_screen(0xFFFFFF);
dm9000_init();
//dm9000_arp();
while(1){
printf("=================================================\n\r");
printf("===================JUN-BOOT======================\n\r");
printf("0.Send the ARP to get yhe host's MAC address\n\r");
printf("1.Download the linux kernel from tftp\n\r");
printf("2.Boot linux OS from SDRAM\n\r");
printf("3.Junjun is a houmorous\n\r");
printf("=================================================\n\r");
printf("===================LCD_TEST======================\n\r");
printf("4.清屏\n\r");
printf("5.画横线\n\r");
printf("6.画竖线\n\r");
printf("7.画十字架\n\r");
printf("8.画同心圆\n\r");
printf("9.AD转换\n\r");
printf("10.Send the ARP to get yhe host's MAC address\n\r");
printf("11.Download the linux kernel from tftp\n\r");
printf("12.Boot linux OS from SDRAM\n\r");
printf(" \n\r");
printf("请输入0-12任意一个数字:\n\r");
scanf("%d",&num);
switch(num){
case 0:
printf("请支持成都国嵌\n\r");
break;
case 1:
printf("国嵌学院=====打造你的嵌入式人生\n\r");
break;
case 2:
printf("学ARM,学Linux,学C++,学安卓,学嵌入式,就到成都国嵌学院\n\r");
break;
case 3:
printf("只要你肯努力,你的明天就会等你!!\n\r");
break;
case 4:
lcd_clear_screen(0x000000);
break;
case 5:
lcd_clear_screen(0x000000);
lcd_draw_hline(HEIGHT/2, 100, WIDTHEIGHT-100, 0xff0000);
break;
case 6:
lcd_clear_screen(0x000000);
lcd_draw_vline(WIDTHEIGHT/2, 50, HEIGHT-50, 0xff0000);
break;
case 7:
lcd_clear_screen(0x000000);
lcd_draw_cross(HEIGHT/2, WIDTHEIGHT/2, 20, 0x777777);
break;
case 8:
lcd_clear_screen(0x000000);
lcd_draw_circle();
break;
case 9:
read_adc(0);
break;
case 10:
arp_request();
break;
case 11:
tftp_request("zImage");//一定要放对位置,而且文件名要一致,目前的程序只支持zImage
break;
case 12:
printf("the function will be completed next time!\n\r");
break;
default:
printf("只要你肯努力,你的明天就会等你!!\n\r");
break;
}
}
return 0;
}