ok6410学习笔记(10.硬件访问之led控制1)

在对于led控制驱动方式上,我看见了6种方式:

在用户空间角度上说有两种:

1.mmap驱动自己写的设备,来把物理地址映射到用户空间。
2.mmap驱动linux内核提供的mem设备,来把物理地址映射到用户空间。

在内核空间角度上说有四种:

1.ioremap的字符设备驱动中或者在混杂设备驱动中的应用,把物理地址映射到内核空间。
2.在不管是国嵌的移植好的内核,还是飞凌移植好的内核中,在/driver/char中都有飞凌写好的驱动,直接调用这个第三方驱动。
3.在linux内核中其实系统早就把地址映射到了内核空间,我们的ioremap只是重新映射了一边,所有我们也可以直接操作这些地址,linux有对S3c6410和S3c2440的映射,还把对2440的gpio操作方法进行了封装。
4.I/O内存静态映射,是在宋保华linux设备驱动中的11.5章的 没有尝试过,不是很了解。

mmap自己的驱动见前面 mmap控制led的那节

mem设备:

/*利用linux自身的设备驱动 mem设备进行 用户空间和物理地址的映射*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

#define uint unsigned int
#define uchar unsigned char
void delay(volatile unsigned int time)
{
	volatile unsigned int x,y;
	for(x=0;x<2000;x++)
		for(y=0;y<time;y++);
}

int main()
{
			int fd;
			int i;
			volatile unsigned char *map;
			volatile unsigned int *GPMCON;
			volatile unsigned int *GPMDAT;
			volatile unsigned int *GPMPUD;
			char buf[100];
			if(-1==(fd=open ("/dev/mem", O_RDWR)))  //要利用mem设备
					 {
	       	 		printf("open dev0 error\n");
	        		_exit(EXIT_FAILURE);
	    		}
	    	/*这个过程是在mem驱动中完成的  map传递的物理地址是通过vma->vm_pgoff即偏移量进行传递的*/
	    	/*但是这里PAGE_SHIFT的问题不知道解决没有   不行就用0x7f008000地址进行传递吧*/
		 map = (volatile unsigned char*)mmap(NULL,1024*5, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x7f008000);
			 if(map == NULL)
     			{
        		printf("mmap err!\n");
	       		return 0;
   			}   
			/*GPMCON=(volatile unsigned int*)(map+0x0);
			GPMDAT=(volatile unsigned int*)(map+0x04);
			GPMPUD=(volatile unsigned int*)(map+0x08);*/
			
			GPMCON=(volatile unsigned int*)(map+0x820);
			GPMDAT=(volatile unsigned int*)(map+0x824);
			GPMPUD=(volatile unsigned int*)(map+0x828);
			
	    		*GPMCON&=~0xffff;
			*GPMCON|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
			*GPMDAT|=0xf;  
			printf("init is finishing!\n");
	   	while(1)
			{
				for(i=0;i<4;i++)
				{
					*GPMDAT=~(1<<i);
					delay(1500);
				}
			}
      munmap((char*)map,1024*5);               
	    close(fd);
}
注意:1.物理地址的参数传递是通过vma->vm_pgoff进行的
   2.貌似还存在PAGE_SHIFT的问题 我试了0x7f008820地址 不行。
此应用程序的驱动在\drivers\char\mem.c里面
static ssize_t write_mem(struct file *file, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	unsigned long p = *ppos;
	ssize_t written, sz;
	unsigned long copied;
	void *ptr;

	if (!valid_phys_addr_range(p, count))
		return -EFAULT;

	written = 0;

#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
	/* we don't have page 0 mapped on sparc and m68k.. */
	if (p < PAGE_SIZE) {
		sz = size_inside_page(p, count);
		/* Hmm. Do something? */
		buf += sz;
		p += sz;
		count -= sz;
		written += sz;
	}
#endif

	while (count > 0) {
		sz = size_inside_page(p, count);

		if (!range_is_allowed(p >> PAGE_SHIFT, sz))
			return -EPERM;

		/*
		 * On ia64 if a page has been mapped somewhere as uncached, then
		 * it must also be accessed uncached by the kernel or data
		 * corruption may occur.
		 */
		ptr = xlate_dev_mem_ptr(p);
		if (!ptr) {
			if (written)
				break;
			return -EFAULT;
		}

		copied = copy_from_user(ptr, buf, sz);
		unxlate_dev_mem_ptr(p, ptr);
		if (copied) {
			written += sz - copied;
			if (written)
				break;
			return -EFAULT;
		}

		buf += sz;
		p += sz;
		count -= sz;
		written += sz;
	}

	*ppos += written;
	return written;
}


你可能感兴趣的:(ok6410学习笔记(10.硬件访问之led控制1))