驱动程序的几个重要问题_第1页
驱动程序的几个重要问题_第2页
驱动程序的几个重要问题_第3页
驱动程序的几个重要问题_第4页
驱动程序的几个重要问题_第5页
已阅读5页,还剩45页未读 继续免费阅读

下载本文档

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

文档简介

1、驱动程序的几个重要问题阳俊开始驱动程序编程的几个前提 必须认真研究硬件电路原理图(全英文版的,在所给文件包里) 必须清楚S3C2410内部寄存器和GPIO口的分配使用情况 要不要使用中断,是不是用查询就够了 使用驱动程序框架,考虑必须使用的几个接口函数1#define GPCON(x) _REG2(0 x56000000, (x) * 0 x10) 这句是定义2410的GPIO的控制寄存器,注意:_REG2的参数是寄存器的物理地址,这个物理地址经_REG2宏转换为虚拟地址,对照2410的手册可以得到一下对应关系: GPCON(1) PORT A 0 x56000000 GPCON(2) POR

2、T B 0 x56000010 GPCON(3) PORT C 0 x56000020 GPCON(8) PORT H 0 x56000070 GPIO口的应用介绍:口的应用介绍:2#define GPDAT(x) _REG2(0 x56000004, (x) * 0 x10) 这句是定义2410的GPIO的数据寄存器,定义方法同GPCON宏。 GPDAT(1) PORT A 0 x56000004 GPDAT(2) PORT B 0 x56000014 GPDAT(3) PORT C 0 x56000024 GPDAT(8) PORT H 0 x56000074 3#define GPUP(

3、x) _REG2(0 x56000008, (x) * 0 x10) 这句是定义2410的GPIO的上拉电阻屏蔽/激活寄存器,定义方法同GPCON宏。 GPUP(1) PORT A 0 x56000008 GPUP(2) PORT B 0 x56000018 GPUP(3) PORT C 0 x56000028 GPUP(8) PORT H 0 x56000078 二、二、GPIO端口号定义端口号定义 以GPIO_G12来说明在内核头文件$(KERNEL_INCLUDE)/asm-arm/arch/s3c2410.h中是如何来定义IO port的端口号的。定义GPIO端口主要涉及到以下几个宏:

4、 #define MAKE_GPIO_NUM(p, o) ( (p GPIO_PORT_SHIFTT) | (o GPIO_OFS_SHIFT) #define GPIO_G12 MAKE_GPIO_NUM(PORTG_OFS, 12) GPIO_PORT_SHIFTT值为8,代表GPIO组号在整个GPIO端口号(如GPIO_G12)字段中的位移 GPIO_OFS_SHIFT值为0,代表GPIO组内偏移号在整个GPIO端口号(如GPIO_G12)字段中的位移s3c2410有117个多功能input/output port pins。分为以下八组: Port A (GPA): 23-output

5、 port #define PORTA_OFS 0 Port B (GPB): 11-input/output port #define PORTB_OFS 1 Port C (GPC): 16-input/output port #define PORTC_OFS 2 Port D (GPD): 16-input/output port #define PORTD_OFS 3 Port E (GPE): 16-input/output port #define PORTE_OFS 4 Port F (GPF): 8-input/output port #define PORTF_OFS 5

6、Port G (GPG): 16-input/output port #define PORTG_OFS 6 Port H (GPH): 11-input/output port #define PORTH_OFS 7 GPG12属于G组,组内偏移为12,从上述两个宏定义中,我们可以很清楚地看出GPIO_G12结构: 三、write_gpio_bit(x,v)宏分析 write_gpio_bit宏传入两个参数,第一个为GPIO端口号,如GPIO_G12;第二个参数为1或0,为相应IO口设置高电平或低电平输出。具体宏展开如下: 代码 #define write_gpio_bit(x, v) (

7、GPDAT(GRAB_PORT(x) &= (0 x1 GRAB_OFS(x); GPDAT(GRAB_PORT(x) |= (v) GPIO_PORT_SHIFTT) 其中GPIO_PORT_MASK是组号的掩码,值为0 x0000ff00,从图1中也可看出。 GRAB_OFS宏和GRAB_PORT类似,它的功能是从GPIO端口号中解析出组内偏移: #define GRAB_OFS(x) (x) & GPIO_OFS_MASK) GPIO_OFS_SHIFT) 其中偏移值掩码GPIO_OFS_MASK0 x000000ff。 现在我们结合上述说明来分析write_gpio_bit(GPIO_

8、G12,1)这条语句:由GPIO_G12的宏定义可计算出其值为0 x0000060C,GRAB_PORT(GPIO_G12)解析得到所操作的IO属于G组,组号为6;GRAB_OFS(GPIO_G12)解析得到此IO口为G组的第12个引脚(从0开始算起),为GPG12,表达式值为12。则write_gpio_bit(GPIO_G12,1)等价于下面两条语句: GPDAT(6) &= (0 x112); /GPGDAT寄存器第12位清零 GPDAT(6) | = 112; / 向GPGDAT寄存器第12位写入1 到此,我们知道了write_gpio_bit(GPIO_G12,1)这条语句是将GPG

9、12这个引脚拉成高电平。四、set_gpio_ctrl(x)宏分析完成了对write_gpio_bit宏的分析,现在来看set_gpio_ctrl就很简单了!在它的宏展开中只多了GRAB_MODE(x)和 GRAB_PULLUP(x)分别表示从参数x中解析出IO口的模式和使能/屏蔽此端口的上拉电阻。值得注意的是set_gpio_ctrl的参数x不仅仅表示GPIO端口号,其高16位还带有模式状态和上拉电阻控制信息,低16位即为前面所述的GPIO的端口号,高16位中的R字段用来屏蔽/使能IO口的上拉电阻功能。R0,上拉电阻使能;R1,上拉电阻失效。M字段用来设置IO口的工作模式,M0,IO口为输入

10、端口;M1,IO口为输出端口;M2,可选功能1;M3,可选功能2。 set_gpio_ctrl宏就是通过写相应GPIO所在组的GPXCON(X为AH)的相应位来设置IO口模式(GPACON每一个位控制一个IO口,而GPBCONGPHCON都是两个位控制一个IO口的模式),通过写GPXUP(X为AH)来决定是否启用上拉电阻。典型的set_gpio_ctrl调用方式如下:set_gpio_ctrl(GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_G12); 代码 #define set_gpio_ctrl(x) ( GPCON(GRAB_PORT(x) &= (0 x

11、3 (GRAB_OFS(x)*2); GPCON(GRAB_PORT(x) |= (GRAB_MODE(x) (GRAB_OFS(x)*2); GPUP(GRAB_PORT(x) &= (1 GRAB_OFS(x); GPUP(GRAB_PORT(x) |= (GRAB_PULLUP(x) GRAB_OFS(x); ) 这条语句是将GPG12设置成输出模式,并且不使用端口的上拉电阻。五、结束 以上主要结合S3C2410X 32-BIT RISC MICROPROCESSOR USERS MANUAL分析了$(LINUX_KERNEL_INCLUDE)/asm-arm/arch/s3c2410.

12、h中所定义的对2410GPIO进行操作的几个宏,除了文中提及的几个宏,除此还有read_gpio_bit(x)、read_gpio_reg(x) 、write_gpio_reg(x, v)等,实现方法和上述类似,在此不再一一赘述! Linux 对中断的处理对中断的处理在Linux 系统中,对中断的处理是属于系统核心部分,因而如果设备与系统之间以中断方式进行数据交换,就必须把该设备的驱动程序作为系统核心的一部分。设备驱动程序通过调用request_irq 函数来申请中断,通过free_irq 来释放中断,它们被定义为:#include int request_irq(unsigned int i

13、rq,void (*handler)(int irq, void dev_id, struct pt_regs *regs);unsigned long flags,const char *device,void *dev_id);void free_irq(unsigned int irq, void *dev_id);参数irq 表示所要申请的硬件中断号;handler 为向系统登记的中断处理子程序,中断产生时由系统来调用,调用时所带参数irq 为中断号;dev_id 为申请时告诉系统的设备标识;regs为中断产生时的寄存器内容;device 为设备名,将会出现在/proc/interru

14、pts 文件里;flag 是申请时的选项,它决定中断处理程序的一些特性,其中最重要的是中断处理程序是快速处理程序还是慢速处理程序。快速处理程序运行时,所有中断都被屏蔽,而慢速处理程序运行时,除了正在运行的中断外,其他中断都没有被屏蔽。在Linux 系统中,中断可以被不同的中断处理程序共享。作为系统核心的一部分,设备驱动程序在申请和释放内存时不是调用malloc 和free,而是kmalloc 和kfree,它们被定义为:#include void *kmalloc(unsigned int len, int priority);void kfree(void *obj);参数len 为希望申请

15、的字节数;obj 为要释放的内存指针;priority 为分配内存操作的优先级,即在没有空闲内存时如何操作,一般用GFP_KERNEL。与中断和内存不同,使用一个没有申请的I/O 端口不会使系统产生异常,也就不会导致诸如“segmentation fault”一类的错误发生。任何进程都可以访问任何一个I/O 端口,此时系统无法保证对I/O 端口的操作不会发生冲突,甚至因此而使系统崩溃,因此,在使用I/O 端口前,也应该检查此I/O 端口是否已经有别的程序在使用。若没有,再把此端口标识为正在使用,在使用完以后释放它。在设备驱动程序中,可以调用printk 来打印一些调试信息,用法与printf

16、类似。Printf 打印的信息不仅出现在屏幕上,同时还记录在文件syslog 里。设备驱动的初始化设备驱动的初始化设备驱动程序所提供的入口点,在设备驱动程序初始化时向系统进行登记,以便系统在适当的时候调用。Linux 系统里,通过调用register_chrdev 向系统注册字符型设备驱动程序。register_chrdev 定义为:#include #include int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);其中,major 是为设备驱动程序向系统申请的主设备

17、号,如果为0,则系统为此驱动程序动态分配一个主设备号。Name 是设备名。Fops 即上述对各个调用的入口点说明。此函数返回0 时表示成功。返回-EINVAL 表示申请的主设备号非法,一般来说是主设备号大于系统所允许的最大设备号。返回-EBUSY 表示所申请的主设备号正在被其他设备程序使用。如果动态分配主设备号成功,此函数将返回所分配的主设备号。如果register_chrdev 操作成功,设备名就会出现在/proc/dvices 文件中。Linux 为每个设备在/dev 目录中建立一个文件,若用ls l 命令列出函数返回值,则小于0表示注册失败;返回0 或者大于0 的值表示注册成功。Linu

18、x kernel 2.0 支持128 个主设备号Linux kernel 2.2 和2.4 支持256 个主设备号(0 和255保留)。注册以后,Linux 把设备名和主/次设备号联系起来。当有对此设备名的访问时,Linux通过请求访问的设备名得到主/次设备号,然后把此访问分发到对应的设备驱动,设备驱动再根据次设备号调用不同的函数。当设备驱动模块从Linux 内核中卸载,对应的主设备号必须被释放。在模块卸载调用cleanup_module()函数时,应该调用下面的函数卸载设备驱动:int unregister_chrdev(unsigned int major, const char *nam

19、e);此函数的参数为主设备号major 和设备名name。Linux 内核把name 和major 在内核注册的名称对比,如果不相等,卸载失败,并返回-EINVAL;如果major 大于最大的设备号,也返回-EINVAL。初始化部分一般还负责给设备驱动程序申请系统资源,包括内存、中断、时钟、I/O 端口等,这些资源也可以在open 子程序或者其他地方申请。这些资源不用时,应该释放,以利于资源的共享。设备驱动的初始化函数只要完成的功能是:(1) 对驱动程序管理的硬件进行必要的初始化对驱动程序管理的硬件进行必要的初始化对硬件寄存器进行设置。比如设置中断掩码,设置串口的工作方式、并口的数据方向等。(

20、2) 初始化设备驱动相关的参数初始化设备驱动相关的参数一般说来,每个设备都要定义一个设备变量,用以保存设备相关的参数。在这里可以对设置变量中的项进行初始化。(3) 在内核注册设备在内核注册设备Linux 内核通过设备的主设备号和从设备号来访问设备驱动,每个驱动程序都有唯一的主设备号。设备号可以自动获取,内核会分配一个独一无二的主设备号,但这样每次获得的主设备号可能不一样,设备文件必须重新建立,所有最好手工给设备分配一个主设备号。可以查看Linux 文件系统中/proc 下的devices 文件,该文件记录内核中已经使用的主设备号和相应的设备名,选择一个没有被使用的主设备号,调用下面的函数来注册

21、设备:int register_chrdev(unsigned int, const char *, struct filr_operations *)其中三个参数分别表示主设备号、设备名称和上面定义的filr_operation 结构地址。该函数是在/linux/include/linux/fs.h 中定义的。(4) 注册中断注册中断如果设备需要IRQ 支持,则要注册中断。注册中断使用函数:in request_irq(unsigned int irq,void (*handler)(int, void *, struct pt_regs *),unsigned long flags,con

22、st char *device,void *dev_id);(5) 其他初始化工作其他初始化工作比如给设备分配I/O。申请DMA 通道等。若驱动程序是内核的一部分,则要按如下方式:int _init chr_driver_init(void);声明,注意不能缺少_init。在系统启动时会由内核调用chr_driver_init ,完成驱动程序的初始化。当驱动程序是以模块的形式编写时,则要按照如下方式:int init_module(void)注:当运行注:当运行insmod 命令插入模块时,会调用命令插入模块时,会调用init_module 函数完成初始化工作函数完成初始化工作。设备驱动程序的

23、开发流程设备驱动程序的开发流程进行嵌入式Linux 系统的开发,很大的工作量是为各种设备编写驱动程序。在ARM 平台上开发嵌入式Linux 的设备驱动程序与在其他平台上开发是一样的。总的来说,实现一个嵌入式Linux 设备驱动的大致流程如下:(1) 查看原理图,理解设备的工作原理(2) 定义主设备号(3) 在驱动程序中实现驱动的初始化。如果驱动程序采用模块的方式,则要实现模块初始化。(4) 设计所要实现的文件操作,定义file_operations 结构。(5) 实现中断服务(中断并不是每个设备驱动所必须的)(6) 编译该驱动程序到内核中,或者用insmod 命令加载(7) 测试该设备内存与I

24、/O访问 内存和I/O的硬件机制 Linux内存管理、内存区域的分布、常规内存与高端内存的区别 Linux内存存取的方法,内存动态申请以及通过虚拟地址存取物理地址的方法 设备I/O内存和I/O端口的访问编程模型1、内存和I/O的硬件机制一些基本的概念:(1)内存空间(2)I/O空间:相对于内存空间而言的,一般与系统架构相关,比如IAX86的in和out指令。其中端口号标识了外设的寄存器地址,当然端口可以是控制口、状态口或数据口中的任何一个。(注意:当然如果让你自己设计电路板,外设仍然可以只挂接内存空间。)IN AX,端口号或DXOUT端口号或DX,AX目前大多数嵌入式微控制器如ARM、Powe

25、rPC等并不提供I/O空间,而仅存在内存空间。内存空间可以直接通过地址,指针来访问,程序和程序运行中使用的变量和其他数据都存在于内存空间中。内存地址可以直接由C语言指针操作,例如在IAX86架构中执行如下代码可以在绝对地址0 xf0000+0 xff00写入11:Usigned char* p=(usigned char*)0 xf000ff00;*p=11而在ARM、PowerPC等未采用段地址的处理器编程模型当中,p指向的内存空间就是0 xf000ff00,而p=11就是往该地址写入11CPU内存指针(地址变量)、数据代码In、out端口号内存空间(必需)I/O空间(可选)外设外设内存空间

26、和I/O空间(3)内存管理单元MMU其基本原理是利用TLB(translation lookaside buffer)转换旁路缓存(又称快表,因为它记录了少量的虚拟地址和物理地址的转换关系)和TTW(translation table walk)转换表漫游(内存中的多级页表)正因为有了MMU,处理器(比如ARM9),Linux操作系统可以提供复杂的存储管理系统,使得进程能访问的内存可以达到4GB内核空间用户空间4GB3GB2GB01GBPAGE_OFFSET这种编程模型使得每个进程的用户空间都是完全独立,互不相干的,用户进程各自有不同的页表。而内核空间是由内核负责映射,它并不会跟着进程改变,是

27、固定的。内核空间地址有自己对应的页表,内核的虚拟空间独立于其他程序。Linux中1GB的内核空间又被划分为物理内存物理内存映射区、虚拟内存分配区、高端页面映射映射区、虚拟内存分配区、高端页面映射区、专用页面映射区和系统保留映射区区、专用页面映射区和系统保留映射区。4GB896MB物理内存映射区高端内存映射区专用页面映射区保留区Vmalloc分配区Linux内核空间物理内存4GB3GB0Linux内核地址空间内存存取相关的系统调用用户空间内存的动态申请:Malloc()和free()内核空间内存的动态申请:kmalloc()、_get_free_pages()和vmalloc()注意:上面系统调

28、用申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,存在简单的转换关系。而最后一个vmalloc()在虚拟空间给出一块连续的内存区,实际上,这片连续的虚拟内存在物理内存中并不一定连续,而vmalloc()申请的虚拟内存和物理内存之间也没有简单的换算关系。对于内核物理内存映射区的虚拟内存,使用virt_to _phys()可以实现内核虚拟地址转化为物理地址;反之,用phys_to _virt可以将物理地址转化成为内核虚拟地址。但是要注意的是,上面的方法仅适用与常规内存,高端内存的虚拟地址与物理地址之间不存在简单的换算关系。设备I/O端口和I/O内存的

29、访问设备通常会提供一组寄存器来用于控制设备、读写设备和获取设备状态,就是我们通常所说的控制寄存器、数据寄存器和状态寄存器。而这些寄存器可能位于I/O空间,也可能位于内存空间。当位于I/O空间时,通常被称为I/O端口,当位于内存空间时,则对应的内存空间被称为I/O内存。Linux的I/O端口和I/O内存访问接口编程模型 1、 I/O端口在Linux设备驱动中,应使用Linux内核提供的函数来访问定位于I/O空间的端口,这些函数包括如下几种:读写字节端口(8位宽)Usigned inb(usigned port)Usigned outb(usigned char byte,usigned port

30、)读写字端口(16位宽)Usigned inw(usigned port)Usigned outw(usigned short word,usigned port)读写长字端口(32位宽)Usigned inl(usigned port)Usigned outl(usigned long word,usigned port)读写一串字节Usigned insb(usigned port,void *addr,usigned long count)Usigned outsb(usigned port,void *addr,usigned long count)以上各个函数中的I/O端口号port

31、类型高度依赖于具体的硬件平台,因此只写出了usigned2、I/O内存在内核中访问I/O内存之前,需首先使用ioremap()函数将设备所处的物理地址映射到虚拟地址。 ioremap()的原型:Void * ioremap(usigned long offset,usigned long size)注意: ioremap()与vmalloc()类似,也需要建立新的页表,但是并不进行vmalloc()中所执行的内存分配行为,只是返回一个特殊的虚拟地址,该地址可用来存取特定的物理地址范围。并且使用的时候一定要注意,通过ioremap()获得的虚拟地址要被iounmap()函数释放,其原型:Void

32、 iounmap(void *addr)在设备的物理地址被映射到虚拟地址后,有两中方法来访问:1、通过指针直接访问2、通过Linux内核提供的组函数来完成设备内存映射的虚拟地址的读写。3、把I/O端口映射到内存空间Void *ioport_map(usigned long port,usigned int count)Void ioport_unmap(void *addr)通过第一个函数,可以把port开始的count个连续的I/O端口重映射成为一段“内存空间”。然后就可以在其返回的地址上象访问I/O内存一样访问I/O端口。当不需要这种映射时,应该调用第二个函数来撤消。实际上,映射到内存空间

33、的行为实际上是给开发人员一种假象,并没有真正映射到内核虚拟地址,仅仅是为了让程序员可使用统一的I/O内存访问接口访问I/O端口。4、申请与释放I/O端口和I/O内存(1)I/O端口申请Linux内核提供了一组函数用于申请和释放I/O端口。Struct resource *request_region(usigned long first,usigned long n,const char *name)成功返回非NULL,如果返回MULL就失败。(2) I/O内存申请同样,Linux也提供了一组函数用于申请和释放I/O内存的范围。Struct resource *request_mem_regi

34、on(usigned long first,usigned long len,const char *name)Void release_mem_region(usigned long first,usigned long len)注意:request_region()和release_mem_region()并不是必须的,但强烈建议使用。其任务是威力检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动程序想再次申请该资源时就会失败。有很多设备驱动程序在没有申请I/O端口和I/O内存之前就访问资源,这样做实际上很不安全!5、设备I/O端口和I/O内存访问流程I/O端口的访问方

35、法:第一种方法是直接使用I/O端口操作函数:在设备打开或驱动模块被加载时就申请I/O端口区域,之后用inb()、outb()等进行端口访问,最后在设备关闭或驱动模块被卸载的时候释放I/O端口范围。第二种方法是将I/O端口映射为内存来进行访问:在设备打开或驱动模块被加载时,申请I/O端口区域并使用ioport_map()映射到内存,之后使用I/O内存的函数进行端口访问,最后在最后在设备关闭或驱动模块被卸载的时候释放I/O端口并释放映射Request_region()inb(),outb()等Release_region()在设备驱动模块加载或open()函数中进行在设备驱动初始化、write()

36、,read(),ioctl()等函数中进行在设备驱动模块卸载或release()函数中进行I/O端口的访问流程(不映射到内存空间)Request_region()inread8(), inread16();iowrite8()等Release_region()在设备驱动模块加载或open()函数中进行在设备驱动初始化、write(),read(),ioctl()等函数中进行在设备驱动模块卸载或release()函数中进行Ioport_map()Ioport_unmap()I/O端口的访问流程(映射到内存空间)I/O内存的访问步骤首先是调用request_mem_region()申请资源,接着是

37、将寄存器地址通过ioremap()进行二次映射,将其映射到内核空间的虚拟地址,之后就可以通过Linux设备访问编程接口访问这些设备的寄存器了。访问完成后,应该对ioremap()申请的虚拟地址进行释放,并释放所申请的I/O内存资源。Request_mem_region()ioread8(), ioread16();iowrite8()等Release_mem_region()在设备驱动模块加载或open()函数中进行在设备驱动初始化、write(),read(),ioctl()等函数中进行在设备驱动模块卸载或release()函数中进行Ioremap()Iounmap()I/O内存访问流程需要

38、注意的地方!(1) 对于I/O断口的操作都是一次传输一个数据,实际上内核也实现了对I/O端口的串操作,从速度上快了许多。不过在新版本的内核中,已经明确指出了这些方法并不推荐使用,估计在新版本的内核中将被淘汰。 上面介绍的三种对I/O的操作方法是有区别的。其中I/O端口空间非常有限(从S3C2410的技术白皮书就可以看到,系统预留的GPIO口是很少的,资源是稀缺的),因此无法满足现在总线的设备。实际上现在的总线设备都以内存映射的方式来映射它的I/O端口和外设内存。需要注意的地方!(2) 但是驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚拟地址空间内,然后才能根据映射所

39、得到的核心虚拟地址范围,通过访内存指令访问这些I/O内存资源。Linux内核提供了ioremap()函数将I/O内存资源的物理地址映射到核心虚拟空间中。对于ARM平台,新版本的内核使用下面几个宏定义完成内存映射,这些宏在asm-arm/io.h文件中,大家有兴趣的话可以去仔细研究。#ifdef _arch_ioremap#define _arch_ioremap#define ioremap(cookie,size) _ioremap(cookie,size,0)#difine ioremap_noCach(cookie,size) _ioremap(cookie,size)#difine ioremap_Cached(cookie,size) _ioremap(cookie,size,LPTE_CACHEABLE)#define iounmap(cookie) _ioumap(cookie)#else#define ioremap(cookie,size) _arch_ioremap(cookie

温馨提示

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

评论

0/150

提交评论