千兆网口FreescaleETSECMarvell_第1页
千兆网口FreescaleETSECMarvell_第2页
千兆网口FreescaleETSECMarvell_第3页
千兆网口FreescaleETSECMarvell_第4页
千兆网口FreescaleETSECMarvell_第5页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、在连续两个平台的uboot和Linux系统移植过程中,在千兆网口调试这块都遇到了很大的麻烦。由于寄存器数量庞大,千兆网口MAC和PHY内部结构复杂,MAC和PHY接口种类多,千兆以太网驱动的调试成了系统移植过程中最让人烦心的一个环节。就像火箭队,每次都让球迷无比揪心,不是输的窝囊,就是伤兵满营,现在新赛季又两连败了,打的比勇士还勇士,后场两个比我还瘦的家伙,怎么防守。算了,不扯这么多了,今天要说的是网口MAC+PHY的一些原理和代码分析。(以Freescale的ETSEC和Marvell的88E1111为例。)1千兆以太网的物理层千兆以太网的物理层分为物理编码子层PCS(PhysicalCod

2、ingSublayer)、物理介质连接子层PMA(PhysicalMediumAttachment)和物理介质相关子层PMD(PhysicalMediumDependent)三层,如下图所示:PC容pwiPMDr4口IJ-MAClOMIIpcs.PHYPHDmdiI/MEiilM?IMMM其中PCS子层负责8b10b编码,它可以把从GMII口接收到的8位并行的数据转换成10位并行的数据输出。因为10比特的数据能有效地减小直流分量,降低误码率,另外采用8b10b编码便于在数据中提取时钟和进行首发同步。可以把PCS两头看成GMII接口和TBI接口。PMA子层进一步将PCS子层的编码结果向各种物理媒

3、体传送,主要是负责完成串并转换。PCS层以125M的速率并行传送10位代码到PMA层,由PMA层转换为1.25Gbps的串行数据流进行发送,以便实际能得到1Gbps的千兆以太网传送速率。可以把PMA子层的两头分别看做TBI接口和SGMII接口。PMD子层将对各种实际的物理媒体完成接口,完成真正的物理连接。由于1000BASE-X支持多种物理媒介,如光纤和屏蔽双绞线,它们的物理接口显然不会相同。有的要进行光电转换,有的要完成从不平衡到平衡的转换。PMD层将对这些具体的连接器作出规定。2Freescale的ETSEC与PHY之间的接口Freescale的MPC8314和P2020都自带了三速以太网

4、控制器ETSEC,可以提供10M,100M,1000M三种速率的接口。当作为以太网时,需要外部的PHY芯片或者Serdes设备与其相连接。每个ETSEC都支持多标准的MII接口,总体结构如下图所示,可以提供GMII,RGMII,MII,RMII,RTBI,SGMII六种接口,下图为从MPC8314datasheet中截取的ETSEC的结构图。如果CPU与PHY之间是GMII接口或RGMII接口,那么PHY将提供完整的PCS,PMA,PMD三层工作;如果CPU与PHY之间是RTBI接口,那么PCS层的工作在ETSEC中已经做完了,ETSEC中的TBI模块可以做PCS层的工作,PHY只需要做PMA

5、和PMD的工作即可;如果CPU与PHY之间是SGMII接口,那么PHY只需要完成PMD的工作,ETSEC中的PCS由TBI完成,而PMA由CPU自带的Serdes模块完成。3BD表结构在千兆以太网的驱动中,现在一般都使用一个叫BD表的东西来管理MAC层发送和接收的内存区域,如下图所示:TbOuflMT-iI品rwd口曲L9tiQrtiMMwRoMvr网WrD*4U*eof541llrtACfr*Ql口LngfhTiBDTqbiwRtwfl*r(TBASCffiTidbtePwthriRBASEjiiTiBDHvlvIbrAngn防BDWfarnBuibrRoi”在IMMR央射的寄存器空间中有两

6、组寄存器TBASE卅RBASEn分别为TxBDRingn和RxBDRingn的指针。MPC8314勺ETSEOfc许有8个TxBDRing和8个RxBDRing,他们都存放在内存的某个区域中。每个BufferDescriptor都是有8个字节构成,两个字节的状态,两个字节的数据长度和四个字节的数据指针,这个指针指向内存的另一块地方,这才是真正存储发送接收数据的地方。BufferDescriptor必须在网口初始化的时候初始化,并将自己的地址赋给TBASErf口RBASEn在网口驱动程序中可以看到,每个BDRing中的BD数量是可变的(我们设为64),而他们之间并没有指针连接,只是一段连续的空间

7、,顺序下来的,所谓的环只是一个虚拟的概念,在最后一个BD时,需要将BD状态位中的W位(Wrap)置一,表示这是最后一个BD他的下一个BD就是第一个BQ如下图所示:下面一节将结合uboot源码分析一下网口初始化以及PHY配置的过程,再下一节会分析内核中的驱动。为什么先说uboot,因为在我看来,驱动程序就是分为两个部分,1按照Datasheet的说明去配置寄存器,2添加符合操作系统规范去融入操作系统。在uboot下系统很简单,代码一目了然,所以我们应该在boot下先把寄存器配置好,再去分析复杂的多的内核代码。这节分析uboot中的网口驱动代码1网口驱动函数列表函数名函数用途tsec_initia

8、lize(网口初始化函数tsec_init(网口启动函数tsec_local_mdio_write(MDIO口写函数tsec_local_mdio_read(MDIO读函数tsec_send(tsec_recv(tsec_configure_serdes(配置TBIPHY的函数fsl_serdes_init(Serdes模块初始化函数网口发送函数网口接收函数init_phy(PHYa始化函数adjust_link(根据PHY犬态配置MAC勺函数2tsec_initialize(函数该函数为ETSEC的初始化函数,在该函数中要初始化eth_device结构和私有的tsec_private结构,并

9、初始化PHY。inttsec_initialize(bd_t*bis,intindex,char*devname(structeth_device*dev;inti;structtsec_private*priv;/*为dev分配空间*/dev=(structeth_device*malloc(sizeof*dev;if(NULL=devreturn0;memset(dev,0,sizeof*dev;/*为priv分配空间*/priv=(structtsec_private*malloc(sizeof(*priv;if(NULL=privreturn0;/*从tsec_info数组中取合适的值

10、去初始化私有结构tsec_private*/每个tsec寄存器的基址privlistnum_tsecs+=priv;priv-regs=tsec_infoindex.regs;priv-phyregs=tsec_infoindex.miiregs;/PHYMDIO读写状态寄存器基址一/*TBIPHY的MDIO读写状态寄存器基址*/priv-phyregs_sgmii=tsec_infoindex.miiregs_sgmii;priv-phyaddr=tsec_infoindex.phyaddr;/PHY地址priv-flags=tsec_infoindex.flags;priv-ID=inde

11、x;/*使用将priv结构体挂到dev结构体下,挂载tsec的打开、关闭、发送、接收函数*/sprintf(dev-name,tsec_infoindex.devname;dev-iobase=0;dev-priv=priv;dev-init=tsec_init;dev-halt=tsec_halt;dev-send=tsec_send;dev-recv=tsec_recv;/*初始化IP地址*/for(i=0;ienetaddri=0;/*设置当前活跃的网口名相当于setethacteTSECn,将多个网口级联*/eth_register(dev;/*通过设置MACCFG1寄存器重启MAC*

12、/priv-regs-maccfg1|=MACCFG1_SOFT_RESET;udelay(2;/*SoftResetmustbeassertedfor3TXclocks*/priv-regs-maccfg1&=(MACCFG1_SOFT_RESET;/*挂载MII口的读写函数*/#ifdefined(CONFIG_MII|defined(CONFIG_CMD_MII/&!defined(BITBANGMIImiiphy_register(dev-name,tsec_miiphy_read,tsec_miiphy_write;#endif/*初始化PHY,并返回*/returninit_phy

13、(dev;)3init_phy(staticintinit_phy(structeth_device*dev(structtsec_private*priv=(structtsec_private*dev-priv;structphy_info*curphy;volatiletsec_t*regs=priv-regs;/*在TBIPA的寄存器中写入TBIPHY的地址*/regs-tbipa=CONFIG_SYS_TBIPA_VALUE+priv-ID;asm(sync;/*重启MII接口*/priv-phyregs-miimcfg=MIIMCFG_RESET;asm(sync;priv-phy

14、regs-miimcfg=MIIMCFG_INIT_VALUE;asm(sync;while(priv-phyregs-miimind&MIIMIND_BUSY;/*通过读PHY的2号3号寄存器获得该ETSEC外连的PHY的ID,搜索phy_info数组,找到符合ID的PHY信息返回。*/curphy=get_phy_info(dev;if(curphy=NULLpriv-phyinfo=NULL;printf(%s:NoPHYfound/n,dev-name;return0;/*如果是SGMII的接口,需要使用TBIPHY,初始化TBIPHY,注意这里名字竟然叫serdes配置,Linux里

15、面也这么叫,真是误人子弟啊。*/if(regs-ecntrl&ECNTRL_SGMII_MODEtsec_configure_serdes(priv;/*在符合条件的PHY的phy_info数组中调用其初始化配置函数*/priv-phyinfo=curphy;phy_run_commands(priv,priv-phyinfo-config;return1;4phy_info结构Uboot中使用这个结构来完成phy的操作,所有的phy都要使用这个结构表示,下面是88E1111的phy_info结的:structphy_infophy_info_M88E1111S=0x01410cc,/PHYI

16、DMarvell88E1111S,/PHY名称4,(structphy_cmd口/*配置数组,在调用priv-phyinfo-config时将依次调用下面的内容,每个大括号内,第一个为PHY寄存器地址,第二个为value*/*ResetandconfigurethePHY*/MIIM_CONTROL,MIIM_CONTROL_RESET,NULL,/*DelayRGMIITXandRX*/MIIM_GBIT_CONTROL,MIIM_GBIT_CONTROL_INIT,NULL,MIIM_ANAR,MIIM_ANAR_INIT,NULL,MIIM_CONTROL,MIIM_CONTROL_RE

17、SET,NULL,MIIM_CONTROL,MIIM_CONTROL_INIT,&mii_cr_init,miim_end,(structphy_cmd/*启动数组,在ETSEC启动的时候要依次调用。*/*Statusisreadoncetoclearoldlinkstate*/MIIM_STATUS,miim_read,NULL,/*Auto-negotiate*/MIIM_STATUS,miim_read,&mii_parse_sr,/*Readthestatus*/MIIM_88E1011_PHY_STATUS,miim_read,&mii_parse_88E1011_psr,miim_

18、end,(structphy_cmd口/*shutdown*/miim_end,;需要注意的是,这个数组时uboot的源码中提供的,但是由于PHY与MAC之间接口使用的不同,这个数组中的内容需要根据需要,参考相应PHY的datasheet作出一定的修改。5tsec_init(该函数不会在初始化的时候调用,它在每当你使用网口的时候被调用,使用网口,不管是ping,还是tftpointtsec_init(structeth_device*dev,bd_t*bduinttempval;chartmpbufMAC_ADDR_LEN;inti;structtsec_private*priv=(struc

19、ttsec_private*dev-priv;volatiletsec_t*regs=priv-regs;/*初始化MACCFG2和ECNTRL两个寄存器,这两个寄存器非常重要,它们主要是用来是配置MAC对PHY的接口。在这里先给个初始化的值,默认为GMII。*/tsec_halt(dev;regs-maccfg2=MACCFG2_INIT_SETTINGS;regs-ecntrl=ECNTRL_INIT_SETTINGS;/*配置MAC地址。*/for(i=0;ienetaddri;)tempval=(tmpbuf024|(tmpbuf116|(tmpbuf2macstnaddr1=temp

20、val;tempval=*(uint*(tmpbuf+4;regs-macstnaddr2=tempval;/*resettheindicestozero*/rxIdx=0;txIdx=0;/*消除其它的寄存器*/init_registers(regs;/*启动tsec*/startup_tsec(dev;/*Iftheresnolink,fail*/return(priv-link?0:-1;)6startup_tsec(staticvoidstartup_tsec(structeth_device*devinti;structtsec_private*priv=(structtsec_pr

21、ivate*dev-priv;volatiletsec_t*regs=priv-regs;aRevisionHistory/*初始化BD表基址指针*/regs-tbase=(unsignedint(&rtx.txbdtxIdx;regs-rbase=(unsignedint(&rtx.rxbdrxIdx;/*初始化RXBD*/for(i=0;iPKTBUFSRX;i+rtx.rxbdi.status=(RXBD_EMPTY|RXBD_INTERRUPT;rtx.rxbdi.length=0;rtx.rxbdi.bufPtr=(uintNetRxPacketsi;)rtx.rxbdPKTBUFS

22、RX-1.status|=RXBD_WRAP;/*初始化TXBD*/for(i=0;iphyinfophy_run_commands(priv,priv-phyinfo-startup;/*根据PHY勺Copper侧值配置MACS存器*/adjust_link(dev;/*使能MACCFG1的发送接收使能*/regs-maccfg1|=(MACCFG1_RX_EN|MACCFG1_TX_EN;/*让DM刖道可以准备赴运了这里的DMA!ETSECJ部的,并不是CPU中的DMAI元。*/regs-dmactrl|=DMACTRL_INIT_SETTINGS;regs-tstat=TSTAT_CLE

23、AR_THALT;regs-dmactrl&=(DMACTRL_GRS|DMACTRL_GTS;参照上面的phy_info数组的startup中的内容得知这里phy_run_commands(priv,priv-phyinfo-startup要调用两个函数mii_parse_sr和mii_parse_88E1011_psr。这两个函数主要是配置三个重要的priv结构体中的成员priv-linkpriv-speedpriv-duplexity分别是link状态,速率和双工。具体的代码就不分析了,主要是读PHY的Copper侧寄存器,然后根据寄存器的值去配置这三个成员,在后面的adjust_lin

24、k函数中会根据这三个成员的值去配置MAC的MACCFG2和ECNTRL寄存器。在uboot阶段,没有挂载中断,接收通过轮询来实现的,所以发送和接收这两个过程跟Linux内核中有区别。在发送阶段,网口将被启动,发送函数首先找到一个可用的BufferDescriptor,将上层软件组好的包的地址赋给该BD的指针,置相应的标志位和长度,然后通知DMA来搬运。搬运结束后,发送函数会消除相应的BD标识位。DMA将数据从内存搬运到TxFIFO后,MAC会给其加上数据链路层的首部后通过GMII口发送到PHY层。在接收阶段,硬件会检测TSECn_RX_DV和TSECn_COL信号,并会检查有效的preambl

25、e,若找到,则检查MAC地址,校验等等,若都合格,则剥掉链路层的包头后,塞给RxFIFO,DMA会将其搬到现在一个有效的RxBD中,我们的接收程序会轮询该BufferDescriptor,直到它有数据时,便将数据提交到上层,然后清除BD的一些状态位。staticinttsec_send(structeth_device*dev,volatilevoid*packet,intlengthinti;structtsec_private*priv=(structtsec_private*dev-priv;volatiletsec_t*regs=priv-regs;/*找一块空的BufferDescr

26、iptor*/for(i=0;rtx.txbdtxIdx.status&TXBD_READY;i+if(i=TOUT_LOOPprintf(%s:tsec:txbuffersfull/n,dev-name;returnresult;rtx.txbdtxIdx.bufPtr=(uintpacket;rtx.txbdtxIdx.length=length;rtx.txbdtxIdx.status|=(TXBD_READY|TXBD_LAST|TXBD_CRC|TXBD_INTERRUPT;/*通过设置寄存器让DMA来从BD中搬运到FIFO中*/regs-tstat=TSTAT_CLEAR_THAL

27、T;/*等到BD搬运完成,清除标志位*/for(i=0;rtx.txbdtxIdx.status&TXBD_READY;i+if(i=TOUT_LOOPprintf(%s:tsec:txerror/n,dev-name;returnresult;txIdx=(txIdx+1%TX_BUF_CNT;result=rtx.txbdtxIdx.status&TXBD_STATS;returnresult;)staticinttsec_recv(structeth_device*devintlength;structtsec_private*priv=(structtsec_private*dev-p

28、riv;volatiletsec_t*regs=priv-regs;while(!(rtx.rxbdrxIdx.status&RXBD_EMPTYlength=rtx.rxbdrxIdx.length;/*有数据来时,检测BD的status,如果没有报错,就扔给上层协议栈*/if(!(rtx.rxbdrxIdx.status&RXBD_STATSNetReceive(NetRxPacketsrxIdx,length-4;elseprintf(Goterror%x/n,(rtx.rxbdrxIdx.status&RXBD_STATS;rtx.rxbdrxIdx.length=0;/*如果是最后一

29、个BD就设置W位*/rtx.rxbdrxIdx.status=RXBD_EMPTY|(rxIdx+1=PKTBUFSRX?RXBD_WRAP:0;rxIdx=(rxIdx+1%PKTBUFSRX;)if(regs-ievent&IEVENT_BSYregs-ievent=IEVENT_BSY;regs-rstat=RSTAT_CLEAR_RHALT;)return-1;)Uboot下的网口驱动就这么多内容。Linux网络驱动设备模型4层,分别是网络协议接口Linux网络设备模型如下图,从上到下可以划分为层,网络设备接口层,设备驱动功能层和网络设备媒介层网络协议接口层向网络层协议提供统一的数据包

30、收发接口,通过dev_queue_xmit(函数发送数据,并通过netif_rx()函数接收数据。网络设备结构层向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device。设备驱动功能层各函数是网络设备结构层net_device数据结构的具体成员,通过hard_start_xmit()函数发送,通过中断触发接收函数。网络设备媒介层就是完成数据包发送和接收的物理实体。套接字缓冲区套接字缓冲区sk_buff的结构体非常重要,用于在Linux网络子系统中的各层之间传递数据,是Linux网络子系统数据传递的“中枢神经”。当发送数据时,Linux内核的网络处理模块必须建立一个包含要

31、传输的数据包sk_buff,然后将sk_buff递交给下层,各层在sk_buff中添加不同的协议头直至交给网络设备发送。口样,当网络设备从网络媒介上向攵数据包后,它必须将接收到的数据转化为sk_buff数据结构并传递给上层,各层剥去相应的协议头,直至交给用户。Skb有四个指针,head和end分别指向数据缓冲区的启始地址和结尾地址,而data和tail分别指向有效数据的开始地址和结尾地址。Skb的操作有:alloc_skb()分配一个套接字缓冲区和一个数据缓冲区;Kree_skb进行套接字缓冲区的释放;skb_push()将data指针上移,主要用于添加协议头部;skb_pull将data指针

32、下移,用于剥去头部。1 dtb文件解析,生成资源单项列表。start_kernelsetup_archunflatten_device_tree该函数可以解析dtb文件,构建一个由device_node结构连接而成的单项链表。如下在此函数执行过后,在内存中会存在一个如下的链表:后面所有的函数,如果需要从oftree结构上读取设备资料的,都将从这个链表中遍历并读取。2 Of_platform总线的注册:Arch/powerpc/kernel/of_platform.cpostcore_initcall(of_bus_driver_init;of_bus_type_init(&of_platfor

33、m_bus_type,of_platformfbus_register(of_platform_bus_type同时:bus-match=of_platform_bus_match;bus-probe=of_platform_device_probe;of_platform_bus_type总线注册完毕。总线的注册/driver/net/Phy_device.csubsys_initcall(phy_initphy_initfmdio_bus_initfbus_register(&mdio_bus_type总线注册后,在总线上注册了一个默认的phy的驱动genphy_driver.phy_id

34、=0xffffffff,.phy_id_mask=0xffffffff,.name=GenericPHY,Mdio总线注册完毕总线上的设备注册:Arch/powerpc/platform/83xx/Mpc831x_rdb.cmachine_device_initcall(mpc831x_rdb,declare_of_platform_devices;declare_of_platform_devicesfof_platform_bus_probe(NULL,of_bus_ids,NULLArch/powerpc/kernel/of_platform.c遍历第一步中在内存中生成链表的所有soc的

35、子节点,将所有的soc子节点设备添加到of_platform总线。of_platform_bus_probefof_platform_device_createfof_device_registerfdevice_addof_platform总线上的所有设备添加完毕,e0024000.ethernet,e0024520.mdio等设备现在都在总线上。5 mdio总线上驱动的添加/driver/net/phy/marvell.cmodule_init(marvell_initmarvell_initphy_driver_register(&marvell_driversidriver_regis

36、ter前面第三步,注册mdio总线后,已经添加了一个默认的phy的驱动,现在要将所有的phy驱动添加到总线上,这里将所有的marvell的phy都添加。这步过后,内核的/sys/bus/mdio/driver里面就有了各种phy的驱动,但这时还没有和具体的设备绑定。6 of_platform总线上Mdio设备驱动(该驱动的目的是在mdio总线上添加PHY设封的添加,并绑定设备:e0024520.mdio和e0025520.mdio/driver/net/fsl_pq_mdio.cmodule_init(fsl_pq_mdio_initfsl_pq_mdio_initof_register_pl

37、atform_driver(&fsl_pq_mdio_driverof_register_driverdriver_registerbus_add_driverdriver_attach遍历整个of_platform总线,寻找与之相匹配的设备,找到e0024520.mdiodriver_attach_driver_attachdriver_match_device将driver的match_table里的信息和dev_nod中的做比较,若符合就进入driver的probe,也就是fsl_pq_mdio_probe。现在of_platform总线上的设备e0024520.mdio和e002552

38、0.mdio已经绑定了驱动。7 Mdio总线上的设备的添加,寻找并绑定相应的驱动。/driver/net/fsl_pq_mdio.cfsl_pq_mdio_probefof_mdiobus_registerfphy_device_registerfdevice_register(&phydev-devfdevice_addfbus_probe_devicefdevice_attachfbus_for_each_drvprobeo扫描mdio总线上的所有的驱动,若找到匹配的,就绑定,并_device_attachfdriver_probe_devicefreally_probefphy_prob

39、e将所有的phy和tbi-phy的设备都添加到mdio总线上,并且两个phy设备和两个tbi-phy设备都会根据其自己的PHYID找到各自的驱动8 of_platform总线上gianfar设备驱动添加,并绑定设备e0024000.ethernet和e0025000.ethernet:/driver/net/gianfar.cmodule_init(gfar_init;gfar_initfof_register_platform_driver(&gfar_driverfof_register_driverfdriver_registerfbus_add_driverfdriver_attach

40、遍历整个of_platform总线,寻找与之相匹配的设备driver_attachf_driver_attachfdriver_match_device将driver的match_table里的信息和dev_nod中的做比较,若符合就进入driver的probe,也就是gfar_probe现在设备e0024000.ethernet和e0025000.ethernet都有了他们自己的驱动。到这步,of_platform上的gianfar设备和mdio设备都有其各自的驱动,mdio总线上的phy设备和tbi-phy设备都有了其驱动程序,但是phy设备和gianfar设备之间还没有任何联系,phy和

41、gianfar都没有初始化。现在要调用相应的驱动去初始化各个设备,连接gianfar和phy。9 gianfar_probe初始化gianfar设备,填充dev和priv结构体。其中gfar_of_init会从of结构中读出priv-phy_node10 phy的初始化,phy和gianfar的连接/net/ipv4/ipconfig.clate_initcall(ip_auto_configip_auto_configic_open_devsdev_change_flags_dev_change_flags_dev_openops-ndo_opengfar_enet_open在gfar设备的

42、打开函数中会去初始化phy,并connecttogianfargfar_enet_openinit_phystaticintinit_phy(structnet_device*dev000000interface=gfar_get_interface(dev;/*PHY连接和初始化*/priv-phydev=of_phy_connect(dev,priv-phy_node,&adjust_link,0,000000/*配置TBI-PHY*/if(interface=PHY_INTERFACE_MODE_SGMIIgfar_configure_serdes(dev;return0;of_phy_connect函数priv-phy_

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论