第13章:WinCE驱动开发与设备管理_第1页
第13章:WinCE驱动开发与设备管理_第2页
第13章:WinCE驱动开发与设备管理_第3页
第13章:WinCE驱动开发与设备管理_第4页
第13章:WinCE驱动开发与设备管理_第5页
已阅读5页,还剩56页未读 继续免费阅读

下载本文档

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

文档简介

嵌入式操作系统第十三章WinCE驱动开发与设备管理戚隆宁longn_qi@WinCE驱动开发与设备管理驱动程序背景知识WinCE5.0的驱动程序WinCE5.0的电源管理(一)驱动程序背景知识什么是驱动程序顾名思义,驱使硬件(虚拟硬件)动作的程序对硬件进行配置对硬件进行控制与硬件进行通信为系统进行硬件功能抽象(一)驱动程序背景知识关于驱动程序的认识误区驱动程序只存在于操作系统中驱动程序的“技术含量”高于应用程序驱动程序没啥“技术含量”,无非是配配寄存器(一)驱动程序背景知识关于驱动程序的认识误区驱动程序只存在于操作系统中intmain()//一个前后台系统的例子{ …

UartInit();

InterruptInit(); while(!UartDataReady); for(i=0;i<UART_FIFO_LENGTH;i++) { *(DataBuf+i)=ReadDataUart(); } …}讨论:这些是驱动程序吗?(一)驱动程序背景知识关于驱动程序的认识误区驱动程序的“技术含量”高于应用程序驱动程序没啥“技术含量”,无非是配配寄存器这两种看法都是片面的,无论是驱动程序还是应用程序,都是算法+数据结构,设计好的程序对开发者都是挑战然而对开发者的要求的确有所不同,设计时考虑的角度也有所区别,程序功能面向的对象也不一样(一)驱动程序背景知识编写驱动程序应具备的基本知识了解驱动程序所“驱动”的硬件了解驱动程序所需要“服务”的系统了解中断、DMA、内存管理等与硬件操作相关的基础机制硬件驱动系统(一)驱动程序背景知识编写驱动程序应具备的基本知识了解驱动程序所“驱动”的硬件USBDriversSDIODriversNetworkingDriversStorageDriversDisplayDriversUserInputDrivers(keypad,touch)AudioDriversSerialDriversBatteryDriversBluetoothDrivers…(一)驱动程序背景知识编写驱动程序应具备的基本知识了解驱动程序所需要“服务”的系统如果是前后台系统,需要由程序员自己考虑驱动程序的架构及驱动程序与系统的接口如果在操作系统中,需要按照操作系统中驱动的架构及驱动与操作系统的接口开发(一)驱动程序背景知识编写驱动程序应具备的基本知识了解中断、DMA、内存管理等与硬件操作相关的基础机制如果是前后台系统,需要由程序员自己设计中断服务程序、完成DMA操作、分配内存如果在操作系统中,需要采用操作系统提供的相关机制进行操作(二)WinCE5.0的驱动程序WinCE5.0驱动程序概述驱动程序在WinCE5.0体系结构中属于OEM层OEM(OriginalEquipmentManufacturer,原始设备制造商)OEM可根据自己产品的硬件特点对WindowsCE进行定制,从而使WindowsCE可以运行在这些厂商的设备上在出售硬件的同时,也会把OEM层以BSP的形式提供给开发者使用体现了WinCE5.0对硬件的适配对体系结构的适配对硬件设备的适配(二)WinCE5.0的驱动程序WinCE5.0驱动程序概述驱动程序在WinCE5.0体系结构中(二)WinCE5.0的驱动程序WinCE5.0驱动程序概述驱动程序在WinCE5.0体系结构中(二)WinCE5.0的驱动程序WinCE5.0驱动程序概述WinCE5.0中驱动程序的主要特点驱动程序实现对硬件的配置、控制、数据的读写通过系统调用向应用程序提供接口(绝大多数情况下为文件操作系统调用)驱动程序在WinCE5.0中表现为动态链接库(dll),被设备管理器进程(或GWES)加载,运行于用户态(二)WinCE5.0的驱动程序WinCE5.0驱动程序概述WinCE5.0中驱动程序的主要特点通过内核态的ISR和驱动程序中特定的中断服务线程(IST)处理中断可以有多个线程,采用WinCE5.0操作系统提供的机制进行线程间的同步采用WinCE5.0操作系统的机制分配内存(二)WinCE5.0的驱动程序WinCE5.0驱动的宿主WinCE5.0中驱动程序表现形式为Dll,因此不能独立运行,必须被加载到某个进程空间(二)WinCE5.0的驱动程序单体驱动与分层驱动单体驱动

所有的驱动程序代码,包括中断、I/O操作、对硬件的控制、资源分配等全部放在一起,与传统驱动开发方式类似分层驱动 分为两层,上面一层叫模型设备驱动(ModelDeviceDriver,MDD),下面一层叫平台相关驱动(PlatformDependenceDriver,PDD)无论是单体驱动还是分层驱动都向宿主导出DDI接口(二)WinCE5.0的驱动程序单体驱动与分层驱动单体驱动的特点内部不分层,包含与驱动硬件直接相关的代码,程序效率高,可移植性差简单驱动采用此种模型结构较为清晰分层驱动的特点MDD层为针对某一类硬件的共性代码,PDD层为针对特定硬件的专用代码MDD与PDD之间通过DDSI(DeviceDriverServiceproviderInterface)接口实现调用可移植性强,针对不同硬件只需修改PDD,但由于存在DDSI调用,效率会有所降低(二)WinCE5.0的驱动程序单体驱动与分层驱动分层驱动的MDD与PDDMDD层通常有如下特性:包含某一类驱动的通用代码,代码可重用生成的Lib库可与不同的PDD库链接调用PDD层访问硬件,定义PDD层的DDSI函数,并在代码中使用提供DDI接口供操作系统访问进行中断处理,中断处理线程IST通常位于这一层通常无需改动PDD层通常有如下特性:包含与某款硬件相关的代码只能与一类MDD协同工作实现MDD的DDSI接口建议尽可能采用分层驱动模型, 结构清晰,可移植性强, 除非对驱动效率要求极高MDD与PDD只是在代码上的逻辑划分, 在运行时是没有这种层次关系的(二)WinCE5.0的驱动程序WinCE5.0的驱动接口类型本地设备驱动对操作系统宿主有一个定制的接口,同时为特定类型的所有设备都给出了一组标准的功能。例如GWES所加载的显示屏、键盘驱动等流式接口驱动适用于输入输出二进制流数据的设备,因此将设备抽象为文件,驱动向宿主导出较为标准的接口,而应用程序通过文件访问系统调用函数访问驱动驱动向宿主导出的接口与文件访问系统调用函数有一定的对应关系是驱动中最为常见的类型,主要由设备管理器宿主(Device.exe)加载混合设备驱动既导出定制的本地接口,又导出流接口。如:USB鼠标等无论哪种都是DDI接口重点需要掌握的是流式接口驱动!(二)WinCE5.0的驱动程序流式接口驱动很多硬件设备都在不断地制造或使用二进制数据,这些设备可被抽象成流式设备WinCE提供流式接口驱动程序管理流式设备。任何暴露流式接口函数的驱动程序都可被称为流式接口驱动程序(把流式接口函数作为DLL的导出函数)在流式接口驱动程序中,驱动程序负责把外设抽象成一个文件,而应用程序则使用操作系统提供的文件API对外设进行访问(二)WinCE5.0的驱动程序流式接口驱动使用示例对串口设备的访问打开设备:HANDLEhComm;hComm=CreateFile(TEXT(“COM1:”),GENERIC_READ|GENERIC_WRITE, 0,NULL,OPEN_EXISTING,0,NULL);写数据:INTrc;DWORDcBytes;BYTEch;ch=TEXT(‘a’);rc=WriteFile(hComm,&ch,1,&cBytes,NULL);设备名(二)WinCE5.0的驱动程序流式接口驱动流式接口函数(向宿主导出)函数名对应的文件操作API功能简述XXX_Open()CreateFile()打开设备以进行操作XXX_Close()CloseHandle()关闭设备XXX_Init()初始化设备,加载时调用XXX_Deinit()释放设备,卸载时调用XXX_Read()ReadFile()从设备中读取数据XXX_Write()WriteFile()向设备中写入数据XXX_Seek()SetFilePointer()移动设备中的数据指针XXX_IOControl()DeviceIOControl()对设备发送控制命令XXX_PowerUp()从挂起恢复时调用XXX_PowerDown()设备关闭自身电源XXX_PreClose()通知驱动程序把打开的句柄设置为效XXX_PreDeinit()通知程序把设备句柄设置为无效XXX为设备名前缀(二)WinCE5.0的驱动程序流式接口驱动XXX_Init()与XXX_Deinit()XXX_Init是驱动程序的动态库被成功装载后第一个被调用的函数(被宿主调用),驱动程序应当在这个函数中初始化硬件(如动态申请逻辑中断号、虚拟地址映射等)。当这个函数成功返回,设备管理器对这个程序就不做进一步处理,至此一个各为XXX的设备就已经加载成功(等待打开操作)XXX_Deinit函数在设备被卸载时调用,它实现与XXX_Init相反的操作,主要为释放前者占用的所有系统资源(二)WinCE5.0的驱动程序流式接口驱动XXX_Open()与XXX_Close()当用户程序调用CreateFile打开设备时,设备管理器就会调用此驱动程序的XXX_Open函数,一般在此函数中完成设备资源的申请当用户程序调用CloseHandle关闭设备时,XXX_Close函数就会被设备管理器调用,它应该做与XXX_Open相反的事情,具体包括:释放XXX_Open分配的内存,将驱动程序被打开的次数减少等(二)WinCE5.0的驱动程序流式接口驱动

XXX_Read()、XXX_Write()与XXX_Seek()它们对设备进行读、写和移动指针的操作。有些设备与应用的交互是单向的,只需要写入或读取数据,因此并不是所有的流式接口设备都需要这三个接口。

XXX_PowerUp()与XXX_PowerDown()电源管理接口,在电源管理部分讲解(二)WinCE5.0的驱动程序流式接口驱动

XXX_IOControl() BOOLXXX_IOControl( DWORDhDeviceContext, DWORDdwCode, PBYTEpBufIn, DWORDdwLenIn, PBYTEpBufOut, DWORDdwLenOut, PDWORDpdwActualOut );几乎可以说一个驱动程序的所有功能都可以在这个函数中实现。当实现一个自定义的设备时,可以随心所欲定义自已的IO操作。一些不适于像文件一样进行读/写的设备,都可通过XXX_IOControl()函数控制。(二)WinCE5.0的驱动程序流式接口驱动流式接口驱动的工作流程(二)WinCE5.0的驱动程序流式接口驱动流式接口驱动的工作流程流式驱动程序工作流程详述如下:(1)加载驱动程序。(2)设备管理器从注册表的dll键值中获取驱动程序所在的DLL文件名。(3)设备管理器调用LoadDriver()函数把XXX.dll加载到自己的虚拟地址空间。(4)设备管理器在注册表记录所有已经加载的驱动程序记录。(5)设备管理器调用驱动的XXX_init函数,对硬件进行基本初始化。经过上面的步骤,驱动成功加载。下面是对驱动程序的操作:(6)应用程序必须先使用CreateFile打开该设备。(7)设备管理器调用驱动的XXX_open()打开设备。(8)设备成功打开后,就可以进行读、写和控制操作。(9)当应用程序不再使用该设备时,调用CloseHandle()将设备关闭。(10)当系统不再使用该设备时,应用程序调用DeactivateDevice()卸载驱动程序。(二)WinCE5.0的驱动程序流式接口驱动将流式接口驱动加入系统(BSP)为流式接口驱动选择一个设备名前缀实现驱动需要的接口函数配置相关文件和注册表加入系统镜像(二)WinCE5.0的驱动程序流式接口驱动将流式接口驱动加入系统(BSP)为流式接口驱动选择一个设备名前缀这里采用由3个大写的英文字母,加一个0-9之间的数字构成的传统方式命名。如:一般打印机设备由特殊文件名“LPTx:”来表示,而串口由特殊文件名“COMx”来表示。以打印机为例,前缀选择为“LPT”实现驱动需要的接口函数可以实现全部或部分接口函数(二)WinCE5.0的驱动程序流式接口驱动将流式接口驱动加入系统(BSP)配置相关文件和注册表

设置相关文件和注册表,使得可以在系统中添加自己的驱动程序在platform/BSP/drivers下新建一个目录,然后在drivers目录中的dirs文件中加入以你刚新建的目录名。在刚新建的目录下,放入驱动程序代码和sources,makefile,mydev.def文件配置注册表(二)WinCE5.0的驱动程序流式接口驱动将流式接口驱动加入系统(BSP)配置相关文件和注册表makefile:只需要一行!INCLUDE$(_MAKEENVROOT)\makefile.def

mydev.def:定义需要输出的函数,这些函数能被其它代码用动态加载的方法调用。格式:LIBRARY

MyDev(这个字符串要和将要生成的动态库的文件名一样)EXPORTS

LPT_Init

LPT_Deinit

LPT_Open

LPT_Close

LPT_PowerOff

LPT_PowerDown

LPT_IOControl(二)WinCE5.0的驱动程序流式接口驱动将流式接口驱动加入系统(BSP) 配置相关文件和注册表

Sources:这个文件很重要,内容也多,最基本的一个文件该有如下内容:TARGETNAME=MyDev

(指定要生成的动态库的名称)TARGETTYPE=DYNLINK(指定要生成的是一个动态库)TARGETLIBS=$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.libSOURCELIBS=$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib\DEFFILE=MyDev.def(指定def文件)DLLENTRY=DllEntry

(指定动态库的入口函数)

SOURCES=(所有源文件名字)(二)WinCE5.0的驱动程序流式接口驱动将流式接口驱动加入系统(BSP) 配置相关文件和注册表

在注册表中添加如下项目:[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\MyDev]

"Prefix"=“LPT"

"Dll"="MyDev.Dll"

"Order"=dword:1驱动程序可以正确地被设备管理器加载。(二)WinCE5.0的驱动程序流式接口驱动将流式接口驱动加入系统(BSP) 将驱动Dll加入系统镜像

加入BIB文件:MODULES;NamePathMemoryType;MyDev.dll

$(_FLATRELEASEDIR)\MyDev.dll

NK

SH(二)WinCE5.0的驱动程序驱动程序的中断处理物理中断与逻辑中断物理中断: 设备可以通过信号线向微处理器发出中断信号,从而请求相应的中断服务。微处理器通过对代表各个中断源的信号线进行编号即得到物理中断号。同样的设备在不同的微处理器和不同的硬件平台上表现出不同的物理中断号。物理中断号在WinCE中通常以IRQ表示逻辑中断: 对物理中断的一种映射,由WinCE以及OEM根据一定规范,为不同的外围设备定义相应的唯一的标识符,即为逻辑中断号。逻辑中断号可以独立于不同的处理器和硬件平台,以使WinCE保持良好的硬件平台无关性。逻辑中断号在WinCE中以SYSINTR表示前缀逻辑中断在WinCE中体现了对硬件物理中断的抽象,可以使驱动的中断处理具有一定的硬件无关性(二)WinCE5.0的驱动程序驱动程序的中断处理物理中断与逻辑中断OAL须把物理中断映射成逻辑中断,然后供操作系统和驱动程序使用 在驱动中实现IRQ和SYSINTR的映射:静态定义#defineIRQ_USBH26#defineSYSINTR_OHCI(SYSINTR_FIRMWARE+1)//17,前16为系统内部使用

OALIntrStaticTranslate(SYSINTR_OHCI,IRQ_USBH);//OAL中动态申请if(!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR,&Irq,sizeof(Irq),&gIntrTouch,sizeof(UINT32),NULL)){

gIntrTouch=SYSINTR_UNDEFINED;

return(FALSE);}(二)WinCE5.0的驱动程序驱动程序的中断处理ISR-IST模型中断处理包括ISR和IST两个部分内核模式中断服务例程(InterruptServerRoutine,ISR)用户模式中断服务线程(InterruptServerThread,IST)(二)WinCE5.0的驱动程序驱动程序的中断处理ISR-IST模型ISR是集成在内核(OAL)中的一段代码提供了对特定物理中断(IRQ)的第一响应为IST的执行保存必要的数据将物理中断转化为逻辑中断通知内核调用相应的中断服务线程为保证系统的实时性,代码非常紧凑,运行时间很短(二)WinCE5.0的驱动程序驱动程序的中断处理ISR-IST模型IST通过创建一个事件对象与逻辑中断关联负责处理特定中断的大部分工作运行在用户态,但具有较高的优先级可以被一个进程或驱动程序创建与系统其他线程共同参与操作系统调度处理完成时,应通知内核再次开启相应的硬件中断(二)WinCE5.0的驱动程序驱动程序的中断处理ISR-IST模型ISR-IST中断处理模型(二)WinCE5.0的驱动程序驱动程序的中断处理ISR-IST模型ISR必须完成中断所需的最少工作:ISR清除该设备上的中断条件ISR向内核返回一个SYSINTR调度等待的IST以执行中断操作大多数工作交给IST去完成:使用CreateEvent()函数创建一个Event内核对象使用InterruptInitialize()把Event与一个逻辑中断关联在Event对象上使用WaitForSingleObject()来等待中断产生中断结束后,必须调用InterruptDone()来完成本次中断处理(二)WinCE5.0的驱动程序驱动程序的中断处理中断处理过程(二)WinCE5.0的驱动程序驱动程序的中断处理中断处理过程(1)一个硬件产生中断时,它被发送到内核的异常处理器(ExceptionHandler),内核处理这个异常;(2)异常处理器调用OAL函数OEMInterruptDisable,通知硬件屏蔽全部中断;(3)ISR向内核返回系统逻辑中断号,之前打开高优先级中断号;(4)内核中的中断服务处理器(InterruptServiceHandler)接受该逻辑中断号并进行后续的处理。一种是什么也不做(SYSINTR_NOP),一种是触发IST(SYSINTR_xxx),另一种是时间片重新调度(SYSINTR_RESHED);(5)内核判断出了中断源,打开此中断源之外的所有中断;(6)内核触发它的中断支持处理器来触发唤醒IST并完成它的工作;(7)IST完成中断处理后,调用InterruptDone()函数通知内核;(8)内核调用OEMInterruptDone完成这个中断所有处理,OAL函数通知硬件重新开启这个中断。(二)WinCE5.0的驱动程序驱动程序的中断处理中断程序中的线程间通信中断线程IST运行在用户态,因此可正常使用WinCE中的线程间通信方式互斥体;信号量;事件;消息方法;临界区;互锁函数(二)WinCE5.0的驱动程序驱动程序的内存访问驱动经常需要访问物理地址,对于驱动程序来说,设备的物理基地址空间必须映射到虚拟内存地址空间,才能实现驱动程序对设备的访问内存管理函数VirtualAlloc()负责在虚拟内存空间保留一段虚拟内存VirtualCopy()负责把一段物理内存和虚拟内存绑定VirtualFree()负责释放所申请的虚拟内存(二)WinCE5.0的驱动程序驱动程序的内存访问例子: 使用VirtualAlloc函数在虚拟地址空间中为一个设备驱动保留4KB的内存块,并返回一个指向这个内存块的指针pVirtualAddr:

pVirtualAddr=(unsigned*)VirtualAlloc(0,0x1000,MEM_RESERVE,//PAGE_NOACCESS); 再使用VirtualCopy函数将上面保留的4KB内存块与设备物理基地址对应:

nTmpVal=VirtualCopy(

pVirtualAddr,(LPVOID)((PhyAddress<<4)/256),0x1000,PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);(二)WinCE5.0的驱动程序驱动程序的内存访问DMA传输时的缓冲区分配 需要为DMA传输分配一块缓冲区,这块缓冲区必须是连续的物理内存。在WinCE中,有两种方法可以分配DMA缓冲区

1.使用CEDDK函数(微软建议.调用内核函数)HalAllocateCommonBuffer():分配缓冲区,成功返回缓冲区的虚拟地址HalFreeCommonBuffer():释放HalAllocateCommonBuffer申请的内存。HalTranslateSystemAddress():把物理内存地址转化为总线相关的逻辑内存地址,然后参数直接传递给DMA控制器使用。

2.使用内核函数AllocPhysMem():申请连续物理内存,成功则返回对应的虚拟地址。FreePhysMem():释放AllocPhysMem申请的内存。(二)WinCE5.0的驱动程序驱动的加载绝大多数驱动都是通过设备管理器加载的(二)WinCE5.0的驱动程序驱动的加载资源管理器系统资源包括IRQ和I/O,是由系统注册表配置和定义的。资源管理器跟踪可用的系统资源,管理驱动程序加载时资源的请求与分配,避免系统的资源冲突总线枚举器由Device.exe加载,通过读取注册表入口来查找新设备。扫描注册表root下的子键,加载更多的总线和设备(三)WinCE5.0的电源管理电源管理器系统利用电源管理器实现电源管理电源管理器负责管理设备电源状态,它提供下列支持:设备可以自己管理电源供应实现电源的宏观控制(系统)和微观控制(设备)

用户可实施自己的电源管理策略

被Device.exe加载(三)WinCE5.0的电源管理电源管理器电源管理模块是以动态连接库的形式存在于系统中的,称为Pm.dll,是Device.exe的加载对象之一为了获得电源管理功能,必须通过PlatformBuilder将电源管理组件编译到内核镜像中:(三)WinCE5.0的电源管理设备电源状态和系统电源状态WinCE提供五种预定义的设备电源状态:D0,D1,D2,D3,D4,它们在注册表中也有相对应的键,如下表所示:设备电源状态注册表键描述FullOnD0电源完全开启LowOnD1低电源或低性能下提供完全功能StandyD2部分供电,等待唤醒请求SleepD3睡眠,提供唤醒的最小电流OffD4未供电所有设备都必须支持的设备电源状态是全开状态D0一般,驱动程序在加载时把设备电源状态设置成D0,卸载时设置成D4(三)WinCE5.0的电源管理设备电源状态和系统电源状态设备电源状态和系统电源状态在common.reg文件中,定义了默认的系统电源状态:系统电源状态描述On用户主动使用设备UserIdle用户与设备停止交互,但有可能仍然使用设备SystemIdle

UserIdle一段时间后进入此状态,但驱动和系统进程仍然活动Suspend驱动和系统进程不再与系统交互时进入此状态ColdReboot和Reboot冷启动系统(三)WinCE5.0的电源管理设备电源状态和系统电源状态系统电源状态代表了设备电源状态的不同组合(三)WinCE5.0的电源管理驱动程序与电源管理器的交互(三)WinCE5.0的电源管理驱动程序与电源管理器的交互电源管理器通过IOCTL代码来和驱动通信 通常情况下,当一个驱动程序声明为支持电源管理时,驱动只需要在XXX_IOControl中实现电源的管理即可。下面是电源管理器向设备发出的电源管理命令:IOCTL_POWER_CAPABILITIES:请求设备驱动返回设备支持的电源状态IOCTL_POWER_SET:请求驱动更新设备的电源状态IOCTL_POWER_QUERY:询问设备是否准备好进行状态切换IOCTL_POWER_GET:请求驱动返回当前设备的电源状态IOCTL_REGISTER_POWER_RELATIONSHIP:通知父设备注册它所控制的设备

其中IOCTL_POWER_CAPABILITIES和IOCTL_POWER_SET是支持电源管理的设备驱动必须实现的(三)WinCE5.0的电源管理一个背光驱动电源管理的例子BOOLBAK_IOControl(

){

switch(dwCode)

{

caseIOCTL_POWE

温馨提示

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

评论

0/150

提交评论