您的位置:首页 > 理论基础 > 计算机网络

DM9000裸机驱动及ARP实现

2015-06-04 12:47 891 查看
一、DM9000物理结构

1、原理图



工作实质就是MAC通过MII控制PHY的过程。

2、网卡和网络模型的映射关系



MAC对应的是数据链路层,PHY对应的是物理层

3、MAC的工作原理



当网络协议栈的IP包送到网卡的时候,先要到达MAC,MAC就根据数据链路层的协议对接收到的数据进行封装,将IP包封装成以太网包,完成数据帧的构建。当然它还具备数据纠错以及传送控制等功能。

4、关于PHY

PHY是物理接口收发器。主要和实际的传输硬件打交道。他接收到来自MAC的以太网包,先加上校检码。然后按照物理层的规则进行数据编码,然后传输到物理介质,接受过程则与之相反。



5、MII即媒体独立接口。表明在MAC一定情况下,更换PHY是不会影响系统的工作的。因为他们最后都要遵循MII接口。故MII起到了MAC和PHY之间通信的桥梁作用。



二、DM9000的编程接口

1、DM9000的接口不是绝对开放的,不能像访问nand控制器那样直接按照地址去访问相关寄存器。

2、但是他提供了两个可以供CPU访问的接口,一个是index另一个是数据端口。

3、index的地址在mini2440上是0x20000300.原因是

(1)mini2440的原理图中



dm9000的片选信号AEN就是接到nLAN_CS片选。再看CPU原理图



我们可以看到nLAN_CS实质接在nGCS4上。再看datasheet



可以看到nGCS4对应的片选信号是0x20000000开头的,在0x20000000-0x28000000之间。所以index的地址开头是0x2********。即片选地址

(2)再看DM9000的datasheet



再看DM9000的TXD【2:0】的引脚接线情况



发现都是0,所以I/O base的地址是300H。即相对片选地址的位置。(相对地址,片选地址相当于基地址)。

(3)不管是index端口还是数据端口都是用SD0-SD15来传递数据。所以要区分具体某一时刻,这些数据到底是给那一个接口用。于是利用CMD引脚来区别。当CMD引脚是高电平的时候,SD上的数据是给数据接口用的,当CMD是低电平的时候,是给index接口用的。index接口是用来传递偏移量的。可以看到CMD接到CPU的ADDR2,当他为1的时候,就是为数据接口传送数据,即地址是0x20000304.当ADDR2为0的时候,SD是为index接口传送偏移量,即ADDR2为0,即地址是0x20000300.故我们找到了,片选地址,I/O base地址,以及数据接口地址。



三、DM9000初始化

1、片选信息设置

(1)数据宽度(BWSCON)

(2)时序信号填写(BANKCON4)

void cs_init()
{
/*1.数据宽度设置*/
BWSCON &= ~(3 << 16);
BWSCON |= (1 <<16);
/*2.时序信号设置*/
BANKCON4 = (0 << 13) | (0 << 11) | (7 << 8) | (1 << 6) | (0 << 4) | (0 << 2) | (0 << 0);
}


2、中断初始化

(1)从原理图找到DM9000使用的中断源

(2)配置相应的中断引脚

(3)设置中断触发方式(高电平,EXTINT0)

(4)使能中断,设置中断屏蔽寄存器(INTMSK,EINTMSK)

(5)清除中断标志(SRCPND,INTPND,EINTPND)

void int_init()
{
/*设置中断引脚工作模式*/
GPFCON = GPFCON &(~(0x3<<14));
GPFCON = GPFCON |(0x2<<14);
/*设置中断触发方式*/
EXTINT0 = EXTINT0 & (~(0x7<<28));
EXTINT0 = EXTINT0 | (0x1<<28);
/*使能中断*/
INTMSK = INTMSK &(~(1<<4));
EINTMASK = EINTMASK & (~(0x1<<7));
/*清楚之前所有的中断标志*/
EINTPEND |= (1<<7);
SRCPND = (1<<4);
INTPND = (1<<4);
}


3、复位设备

(1)实现往DM9000读写数据的函数

(2)设置I/O为输出模式

(3)通过对GPIO0写0为内部的PHY提供电源

(4)软件复位(自动清0),MAC内部回环模式

(5)对(4)中的寄存器全部写入0

(6)重复(4)(5)

void dm9000_write(u16 reg,u16 data)
{
DM_ADD = reg;
DM_DAT = data;
}
u8 dm9000_read(u16 reg)
{
DM_ADD = reg;
return DM_DAT;
}
void dm9000_reset()
{
/*1.设置I/O为输出模式*/
dm9000_write(DM9000_GPCR, GPCR_GPIO0_OUT);
/*2.通过对GPIO0写入0为内部的PHY提供电源*/
dm9000_write(DM9000_GPR, 0);
/*3.软件复位(自动清0),MAC内部回环模式*/
dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));
/*4.对上一步的寄存器写入全0*/
dm9000_write(DM9000_NCR, 0);
/*5.重复(3)(4),用两次实现真正复位*/
dm9000_write(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));
dm9000_write(DM9000_NCR, 0);
}


4、捕获网卡

(1)读取厂家ID

(2)读取product的ID

(3)将两个ID组合与之前预定义的网卡ID进行对比

u8 dm9000_probe()
{
u32 id_val;
/*1.读取厂家ID*/
id_val = dm9000_read(DM9000_VIDL);
id_val |= dm9000_read(DM9000_VIDH) << 8;
/*2.读取产品ID并将其和厂家ID组合*/
id_val |= dm9000_read(DM9000_PIDL) << 16;
id_val |= dm9000_read(DM9000_PIDH) << 24;

if (id_val == DM9000_ID) {
printf("dm9000 is found !\n\r");
return 0;
} else {
printf("dm9000 is not found !\n\r");
return -1;
}
}


5、MAC初始化

参照u-boot设置MAC

void dm9000_mac_init()
{
/* Program operating register, only internal phy supported */
dm9000_write(DM9000_NCR, 0x0);
/* TX Polling clear */
dm9000_write(DM9000_TCR, 0);
/* Less 3Kb, 200us */
dm9000_write(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);
/* Flow Control : High/Low Water */
dm9000_write(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
/* SH FIXME: This looks strange! Flow Control */
dm9000_write(DM9000_FCR, 0x0);
/* Special Mode */
dm9000_write(DM9000_SMCR, 0);
/* clear TX status */
dm9000_write(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
/* Clear interrupt status */
dm9000_write(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);
}


6、填充MAC地址

利用循环分别填写六个寄存器,对应MAC地址的6个段

void dm9000_fill_macadd()
{
u16 oft = 0,i = 0;
/* fill device MAC address registers */
for (i = 0; i < 6; i++)
dm9000_write(DM9000_PAR + i, mac_addr[i]);
/*maybe this is some problem*/
for (i = 0, oft = 0x16; i < 8; i++, oft++)
dm9000_write(oft, 0xff);
/* read back mac, just to be sure */
for (i = 0, oft = 0x10; i < 6; i++, oft++)
printf("%02x:", dm9000_read(oft));
printf("\n\r");

}


7、激活DM9000

参照u-boot设置相关寄存器即可。

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);

}


8、初始化代码

void dm9000_init()
{
/*1.片选信息设置*/
dm9000_cs();
/*2.中断初始化*/
dm9000_int_init();
/*3.复位设备*/
dm9000_reset();
/*4.捕获网卡*/
if (dm9000_probe() < 0)
return ;
/*5.MAC初始化*/
dm9000_mac_init();
/*6.填充MAC地址*/
dm9000_fill_macadd();
/*7.激活DM9000*/
dm9000_active();
}


四、发送函数(有两个参数,待发送数据和发送长度)

1、禁止中断,避免干扰

(1)DM9000的IMR寄存器。

2、写入发送数据的长度

将长度分两次写入寄存器

3、写入待发送的数据

(1)将MWCMD赋值给地址端口,做好准备,MWCMD会自动将数据送到TX SRAM中。

(2)利用循环,将数据写入数据端口

4、启动发送

往TCR寄存器中写入命令,请求开始发送

5、等待发送结束

当发送结束的时候,TCR的0位会自动清0,所以去等待他变0即可。

6、判断发送结果是否正确,清除发送状态

往NSR寄存器中写入0x2C即可

7、打开中断,等待接收数据

将IMR的中断打开即可。(设置最后一位为1即可)

void dm9000_tx(u8* data, u32 length)
{
u32 i = 0;
/*1.禁止中断。避免干扰*/
dm9000_write(DM9000_IMR,0x80);
/*2.清除发送标志位*/
dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
/*3.写入发送数据长度*/
dm9000_write(DM9000_TXPLL, length & 0xff);
dm9000_write(DM9000_TXPLH, (length >> 8) & 0xff);
/*4.写入待发送数据*/
DM_ADD = DM9000_MWCMD;
for(i=0;i<length;i+=2)
{
DM_DAT = data[i] | (data[i+1]<<8);
}
/*5.启动发送*/
dm9000_write(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
/*6.等待发送完成*/
while(dm9000_read(DM9000_TCR) & 0x01);
/*7.清除发送状态*/
dm9000_write(DM9000_NSR,0x2c);
/*8.清除发送标志位*/
dm9000_write(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
/*9.使能接收中断*/
dm9000_write(DM9000_IMR,0x81);
}


五、接收函数

接收是中断处理的,接收到一个包就会产生中断。在中断处理的时候调用接收函数。

1、判断是否产生中断,是就继续,否则退出接收函数

读取ISR寄存器第0位即可。

2、清除中断

ISR寄存器第0位写1即可。

3、空读

读取MRCMDX寄存器

4、读取包的状态和长度

读取MRCMD寄存器得到状态,此时地址端口的数据就是对应MRCMD的偏移量,所以可以直接读取此时数据寄存器的值,不用再重新指定偏移量,就可以得到长度。在长度后面会自动送入有效的数据,所以后面可以页直接读数据寄存器得到有效数据。

5、读取包的数据

在读取数据之前应该对读到的长度进行检查,看是否小于以太网包的最大长度。然后利用for循环读取数据,注意数据的组合方式。

u16 dm9000_rx(u8* data)
{
u32 i = 0;
u16 status;
u16 tmp,len;
u8 ready;

/*1.判断是否产生中断*/
if (!(dm9000_read(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */
return 0;

/*2.清除中断*/
dm9000_write(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */

/*3.空读*/
ready = dm9000_read(DM9000_MRCMDX);
/*if((ready & 0x01) != 0x01){
ready = dm9000_read(DM9000_MRCMDX);
if ((ready & 0x01) != 0x01)
return 0;
}*/

/*4.读取包的状态和长度*/
status = dm9000_read(DM9000_MRCMD);
len = DM_DAT;

/*5.读取包的数据*/
if(len<DM9000_PKT_MAX)
{
for(i=0;i<len;i+=2)
{
tmp = DM_DAT;
data[i] = tmp & 0x0ff;
data[i+1] = (tmp>>8)&0x0ff;
}
}
return len;
}


六、中断处理函数

1、调用接收函数存放接收到的数据

2、清除中断标志(SRCPND,INTPND,EINTPND)。

void int_issue()
{

packet_len = dm9000_rx(&buffer[0]);

net_handle(&buffer[0],packet_len);

EINTPEND |= (1 << 7);
SRCPND |= (1 << 4);
INTPND |= (1 << 4);
}


七、ARP协议实现

1、以太网通讯格式

在计算机网络中,根据不同层的协议对要传输的数据进行封装,最重要实现的协议通常是以太网协议,即数据链路层协议。以太网包格式。



2、ARP功能简介

在计算机网络中,MAC地址是物理层的地址,是计算机唯一的身份标识。两台计算机要实现通信,必须知道彼此的MAC地址,但是一般情况用户只知道目标机的IP地址,这个时候,就可以用ARP(地址解析协议)来向局域网中的所有计算机发送ARP请求包,当然这个请求包有目标机的IP地址,符合条件的目标机接收到请求包以后,会作出应答,不符合条件的忽略该请求包。在应答包中目标机会将自己的MAC地址反馈给用户。所以ARP是一种通过IP地址获取MAC地址的协议。

3、ARP包格式



4、代码实现

(1)发送ARP请求包

–》构造请求包

– –》ARP帧类型是0806

–》当未知MAC地址的时候,设置为全F

–》硬件类型是指硬件地址对应的类型,即以太网类型,编号为1

–》协议类型是指协议地址对应的类型,在这里协议地址是IP地址,IP类型的编号是0800

–》硬件地址长度即以太网地址长度,6个字节

–》协议地址长度即Ip地址长度4个字节

–》op为1表示请求,为0表示应答

–》发送端以太网地址就是以太网源地址,事先制定的,6个字节

–》发送端Ip地址是事先制定的,4个字节,注意:要和PC在同一网段

–》目的以太网地址,暂时不填,要获取的就是该数据

–》目的Ip地址就是PC的Ip实际地址。

–》调用DM9000发送函数发送包

—-》可以讲上述构建的ARp请求包的地址赋值给u8 的指针,让该数组指向该请求包

—-》然后发送长度为:(以太网)14 + (ARP真正请求部分)28.

–》头文件对数据结构进行创建

#ifndef __ARP_H__
#define __ARP_H__

typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;

#define SWAP(n) ((((u16)n & 0xff) << 8) | ((((u16)n >> 8) & 0xff)))

/*以太网头部结构体*/
typedef struct eth_header{
u8 d_mac[6];
u8 s_mac[6];
u16 frame_type;

}ETH_HDR;

/*ARP头部结构体*/
typedef struct arp_header{
ETH_HDR ethhdr;
u16 hw_type;
u16 protocol;
u8 hwadd_len;
u8 protoc_len;
u16 opcode;
u8 smac[6];
u8 sipaddr[4];
u8 dmac[6];
u8 dipaddr[4];

}ARP_HDR;

/*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头部结构体*/
typedef struct udp_hdr
{
IP_HDR iphdr;
u16 sport;
u16 dport;
u16 len;
u16 udpchksum;
}UDP_HDR;

/*TFTP数据包结构体*/
typedef struct tftp_package
{
u16 opcode;
u16 blocknum;
u8 data[0];
}TFTP_PAK;

ARP_HDR arpbuf;

/*网络协议类型*/
#define PROTO_ARP 0x0806
#define PROTO_IP 0x0800
#define PROTO_UDP 0x11

extern u8 host_mac_addr[6];
extern u8 mac_addr[6];
extern u8 ip_addr[4];
extern u8 host_ip_addr[4];
extern u16 packet_len;
#endif


那些extern变量在dm9000.c里定义,根据自己PC的IP来决定host_ip_addr

u8 host_mac_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
u8 mac_addr[6] = {9,8,7,6,5,4};
u8 ip_addr[4] = {192,168,1,30};//根据自己的电脑设置,要和电脑IP在同一网段
u8 host_ip_addr[4] = {192,168,1,100};//根据自己的电脑的IP设置
u16 packet_len;


(2)在网络中默认主机是大端格式的,涉及到网络字节序。所以在发送端存储数据的时候要按照大端格式存储。所以整个包都要征对象的修改,地址可以不用改,但是数据要改。只针对由两个字节构成的数据,一个字节长度的不用改。涉及到地址都会用到memcpy函数。

void arp_request()
{
/*1.构成ARP请求包*/

memcpy(arpbuf.ethhdr.d_mac,host_mac_addr,6);    //以太网目的地址
memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);     //以太网源地址
arpbuf.ethhdr.frame_type = SWAP(0x0806);        //帧类型

arpbuf.hw_type = SWAP(1);               //硬件类型
arpbuf.protocol = SWAP(0x0800);         //协议类型

arpbuf.hwadd_len = 6;               //硬件地址长度
arpbuf.protoc_len = 4;              //协议地址长度

arpbuf.opcode = SWAP(1);                //操作码

memcpy(arpbuf.smac,mac_addr,6);         //发送端以太网地址,即以太网原地址
memcpy(arpbuf.sipaddr,ip_addr,4);       //发送端IP地址

memcpy(arpbuf.dipaddr,host_ip_addr,4);      //目的IP地址

packet_len = 14+28;             //包的长度

/*2.调用dm9000发送函数,发送请求包*/
dm9000_tx((u8*)&arpbuf,packet_len);
}


(3)解析ARP应答包,提取目标机MAC地址。

–》返回类型为u8

–》打印信息时候注意格式控制

u8 arp_respond(u8* buf,u32 len)
{
ARP_HDR* p = (ARP_HDR*)buf;
u32 i = 0;

if(packet_len < 28)
return 0;

switch(SWAP(p->opcode)){
case 2://对PC发到开发板的应答包解析
memcpy(host_ip_addr,p->sipaddr,4);
printf("host ip is : ");
for(i=0;i<4;i++)
printf("%03d ",host_ip_addr[i]);
printf("\n\r");

memcpy(host_mac_addr,p->smac,6);
printf("host mac is : ");
for(i=0;i<6;i++)
printf("%02X ",host_mac_addr[i]);
printf("\n\r");
break;
case 1://响应PC发到开发板的请求包
memcpy(arpbuf.ethhdr.d_mac,p->ethhdr.s_mac,6);  //以太网目的地址
memcpy(arpbuf.ethhdr.s_mac,mac_addr,6);     //以太网源地址
arpbuf.ethhdr.frame_type = SWAP(0x0806);        //帧类型

arpbuf.hw_type = SWAP(1);               //硬件类型
arpbuf.protocol = SWAP(0x0800);         //协议类型

arpbuf.hwadd_len = 6;               //硬件地址长度
arpbuf.protoc_len = 4;              //协议地址长度

arpbuf.opcode = SWAP(2);                //操作码

memcpy(arpbuf.smac,mac_addr,6);         //发送端以太网地址,即以太网原地址
memcpy(arpbuf.sipaddr,ip_addr,4);       //发送端IP地址

memcpy(arpbuf.dmac,p->smac,6);
memcpy(arpbuf.dipaddr,p->sipaddr,4);        //目的IP地址

packet_len = 14+28;             //包的长度

/*2.调用dm9000发送函数,发送请求包*/
dm9000_tx((u8*)&arpbuf,packet_len);

break;

default:
break;

}

return 1;
}


附:dm9000.c相关寄存器地址

/*
* dm9000 Ethernet
*/
#ifndef __DM9000_H__
#define __DM9000_H__

#define DM9000_ID       0x90000A46
#define DM9000_PKT_MAX      1536    /* Received packet max size */
#define DM9000_PKT_RDY      0x01    /* Packet ready to receive */

/* although the registers are 16 bit, they are 32-bit aligned.
*/

#define DM9000_NCR             0x00
#define DM9000_NSR             0x01
#define DM9000_TCR             0x02
#define DM9000_TSR1            0x03
#define DM9000_TSR2            0x04
#define DM9000_RCR             0x05
#define DM9000_RSR             0x06
#define DM9000_ROCR            0x07
#define DM9000_BPTR            0x08
#define DM9000_FCTR            0x09
#define DM9000_FCR             0x0A
#define DM9000_EPCR            0x0B
#define DM9000_EPAR            0x0C
#define DM9000_EPDRL           0x0D
#define DM9000_EPDRH           0x0E
#define DM9000_WCR             0x0F

#define DM9000_PAR             0x10
#define DM9000_MAR             0x16

#define DM9000_GPCR        0x1e
#define DM9000_GPR             0x1f
#define DM9000_TRPAL           0x22
#define DM9000_TRPAH           0x23
#define DM9000_RWPAL           0x24
#define DM9000_RWPAH           0x25

#define DM9000_VIDL            0x28
#define DM9000_VIDH            0x29
#define DM9000_PIDL            0x2A
#define DM9000_PIDH            0x2B

#define DM9000_CHIPR           0x2C
#define DM9000_SMCR            0x2F

#define DM9000_PHY      0x40    /* PHY address 0x01 */

#define DM9000_MRCMDX          0xF0
#define DM9000_MRCMD           0xF2
#define DM9000_MRRL            0xF4
#define DM9000_MRRH            0xF5
#define DM9000_MWCMDX           0xF6
#define DM9000_MWCMD           0xF8
#define DM9000_MWRL            0xFA
#define DM9000_MWRH            0xFB
#define DM9000_TXPLL           0xFC
#define DM9000_TXPLH           0xFD
#define DM9000_ISR             0xFE
#define DM9000_IMR             0xFF

#define NCR_EXT_PHY     (1<<7)
#define NCR_WAKEEN      (1<<6)
#define NCR_FCOL        (1<<4)
#define NCR_FDX         (1<<3)
#define NCR_LBK         (3<<1)
#define NCR_LBK_INT_MAC     (1<<1)
#define NCR_LBK_INT_PHY     (2<<1)
#define NCR_RST         (1<<0)

#define NSR_SPEED       (1<<7)
#define NSR_LINKST      (1<<6)
#define NSR_WAKEST      (1<<5)
#define NSR_TX2END      (1<<3)
#define NSR_TX1END      (1<<2)
#define NSR_RXOV        (1<<1)

#define TCR_TJDIS       (1<<6)
#define TCR_EXCECM      (1<<5)
#define TCR_PAD_DIS2    (1<<4)
#define TCR_CRC_DIS2    (1<<3)
#define TCR_PAD_DIS1    (1<<2)
#define TCR_CRC_DIS1    (1<<1)
#define TCR_TXREQ       (1<<0)

#define TSR_TJTO        (1<<7)
#define TSR_LC          (1<<6)
#define TSR_NC          (1<<5)
#define TSR_LCOL        (1<<4)
#define TSR_COL         (1<<3)
#define TSR_EC          (1<<2)

#define RCR_WTDIS       (1<<6)
#define RCR_DIS_LONG    (1<<5)
#define RCR_DIS_CRC     (1<<4)
#define RCR_ALL         (1<<3)
#define RCR_RUNT        (1<<2)
#define RCR_PRMSC       (1<<1)
#define RCR_RXEN        (1<<0)

#define RSR_RF          (1<<7)
#define RSR_MF          (1<<6)
#define RSR_LCS         (1<<5)
#define RSR_RWTO        (1<<4)
#define RSR_PLE         (1<<3)
#define RSR_AE          (1<<2)
#define RSR_CE          (1<<1)
#define RSR_FOE         (1<<0)

#define EPCR_EPOS_PHY       (1<<3)
#define EPCR_EPOS_EE        (0<<3)
#define EPCR_ERPRR      (1<<2)
#define EPCR_ERPRW      (1<<1)
#define EPCR_ERRE       (1<<0)

#define FCTR_HWOT(ot)   (( ot & 0xf ) << 4 )
#define FCTR_LWOT(ot)   ( ot & 0xf )

#define BPTR_BPHW(x)    ((x) << 4)
#define BPTR_JPT_200US      (0x07)
#define BPTR_JPT_600US      (0x0f)

#define IMR_PAR         (1<<7)
#define IMR_ROOM        (1<<3)
#define IMR_ROM         (1<<2)
#define IMR_PTM         (1<<1)
#define IMR_PRM         (1<<0)

#define ISR_ROOS        (1<<3)
#define ISR_ROS         (1<<2)
#define ISR_PTS         (1<<1)
#define ISR_PRS         (1<<0)

#define GPCR_GPIO0_OUT      (1<<0)

#define GPR_PHY_PWROFF      (1<<0)

#endif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络