天天看点

《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器

Ok6410开发板LED连接电路图:

《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器

           从电路图上我们可以看到,发光二极管LED 的一端连接到了ARM 的GPIO,另一端经过一个限流电阻接电源VCC3。当GPIO 口为低电平时,LED 两端产生电压降,这时LED 有电流通过并发光。反之当GPIO 为高电平时,LED 将熄灭。注意亮灭之间要有一定的延时,以便人眼能够区分出来。

4个LED分别连接到核心板上的GPM端:

《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器

从上面可以看出4个LED对应的端口:

GPM0->LED1  GPM1->LED2 GPM2->LED3  GPM3->LED4

查看s3c6410芯片手册,端口M对应的三个寄存器地址:

                    实验相应寄存器

《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器

                    端口配置寄存器

《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器

                    端口数据寄存器

《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器

下面我们开始代码部分

I/O 内存访问流程:

      1. request_mem_region()  申请IO内存

      2. ioremap() 将物理地址映射到虚拟地址

      3. ioread8() 、ioread16()、ioread32()、iowrite8()、iowrite16()、iowrite32() 读写

     4. iounmap() 释放虚拟内存

     5. release_mem_region() 释放IO内存

注意:

     1、2 在模块初始化或打开设备时调用

     4、5 在模块卸载或关闭设备时调用

         request_mem_region()不是必须使用,但建议使用。任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再次申请该资源就会失败。

testled.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "liye_led"

/* 定义幻数 */
#define LED_IOC_MAGIC  'k'

/* 定义命令 */
#define LED_ALLOFF   _IO(LED_IOC_MAGIC, 0)
#define LED_ALLON    _IO(LED_IOC_MAGIC, 1)
#define LED_SELECTON _IOW(LED_IOC_MAGIC, 2, int)

/*定义命令的最大个数*/
#define LED_IOC_MAXNR 2

#define GPMCON 0x7F008820 //寄存器地址(物理地址)
#define GPMDAT 0x7F008824

static volatile unsigned long *gpmcon_addr; //经过ioremap映射后的虚拟地址
static volatile unsigned long *gpmdat_addr;

static void Led_port_init(void)
{
	//设置GPM0-GPM3为输出端口
	//最后结果相当于…0001000100010001,都是输出状态
	*gpmcon_addr &= (1U<<16); 
	*gpmcon_addr |= (1U<<0)|(1U<<4)|(1U<<8)|(1U<<12);
   
	//全亮   低电平亮
	*gpmdat_addr &= (1U<<4);
}

static void led_all_on(void )
{
//	*gpmdat_addr &= (1U<<4);这个也是可以的,一样的
	*gpmdat_addr &= 16; 
}

static void led_all_off(void)
{
	*gpmdat_addr |= ~(1U<<4);
}

static void led_select_on(unsigned int led_nu)
{
	led_all_off();
	*gpmdat_addr &= ~(led_nu);
}

static int led_open(struct inode * inode , struct file * filp)
{
	return 0;
}

static int led_release(struct inode * inode, struct file *filp)
{
	return 0;
}

static int led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	int err = 0;
	int ioarg = 0;

   /* 检测命令的有效性 */
    if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) 
        return -EINVAL;
    if (_IOC_NR(cmd) > LED_IOC_MAXNR) 
        return -EINVAL;

   /* 根据命令类型,检测参数空间是否可以访问 */
    if (_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
    if (err) 
        return -EFAULT;

	switch (cmd)
	{
		case LED_ALLOFF://全灭
			led_all_off();
			break;
		case LED_ALLON://全亮
			led_all_on();
			break;
		case LED_SELECTON://根据参数有选择的凉
			__get_user(ioarg,(int *)arg);//获得用户空间数据
			led_select_on(ioarg);
			break;
		default:
			ret = -EINVAL;
			break;
	}
	return ret;
}

static const struct file_operations led_fops =
{
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_release,
	.unlocked_ioctl = led_ioctl,
};

static struct miscdevice led_dev =
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &led_fops,
};

static int __init led_init(void)
{ 
	int ret;
 
	//申请IO内存,不是必须的。
	if (!request_mem_region(GPMCON, 8, "leds"))
	{
		ret = -EBUSY;
		goto request_mem_failed;
	}
 
	gpmcon_addr = ioremap(GPMCON, 4);//将物理地址映射为虚拟地址
	if (NULL == gpbcon_addr)
	{
		ret = -EIO;
		printk("gpmcon remap failed\n");
		goto con_map_failed;
	}
	gpmdat_addr = ioremap(GPMDAT, 4);
	if (NULL == gpmdat_addr)
	{
		ret = -EIO;
		printk("gpmdat remap failed\n");
		goto dat_map_failed;
	}
	printk("gpmcon_addr remap on %p\n", gpmcon_addr);
	printk("gpmdat_addr remap on %p\n", gpmdat_addr);
 
	Led_port_init();//初始化led灯

	ret = misc_register(&led_dev);//注册一个混杂设备驱动,主设备号为10
	if (ret)
	{
	printk("misc_register failed\n");
	goto failed;
	}
 
	printk("leds init\n");
	return 0;
 
failed:
	iounmap(gpmdat_addr);
dat_map_failed:
	iounmap(gpmcon_addr);
con_map_failed:
	release_mem_region(GPMCON, 8);
request_mem_failed:
	return ret;
}

static void __exit led_exit(void)
{
	iounmap(gpmdat_addr); //取消映射
	iounmap(gpmcon_addr);
	release_mem_region(GPMCON, 8); //释放I/O内存
	misc_deregister(&led_dev);//注册混杂设备,主设备号为10
 
	printk("leds exit\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("liye");
module_init(led_init);
module_exit(led_exit);
           

test.c

#include <stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <linux/ioctl.h>

/* 定义幻数 */
#define LED_IOC_MAGIC  'k'

/* 定义命令 */
#define LED_ALLOFF   _IO(LED_IOC_MAGIC, 0)
#define LED_ALLON    _IO(LED_IOC_MAGIC, 1)
#define LED_SELECTON _IOW(LED_IOC_MAGIC, 2, int)

int main()
{
    int fd = 0;
    int cmd;
    int arg = 0;
    int choice;
    int flag=0;
	
    /*打开设备文件*/
    fd = open("/dev/liye_led",O_RDWR);
    if (fd < 0)
    {
        printf("Open Dev liye_led Error!\n");
        return -1;
    }
    
    while(1)
    {
	printf("\n********MENU**********\n");
	printf("     [0] LED all off    \n");
	printf("     [1] LED all on     \n");
	printf("     [2] Select LED     \n");
	printf("     [3] Exit           \n");
	printf("**********************\n\n");
	printf("Enter the choice: ");
	scanf("%d",&choice);

	switch(choice)
	{
		case 0:
			cmd=LED_ALLOFF;
			break;
		case 1:
			cmd=LED_ALLON;
			break;
		case 2:
			printf("Enter the arg (0=<arg<=15) : ");
			scanf("%d",&arg);
			cmd=LED_SELECTON;
			break;
		case 3:
			flag=1;
			break;
		default:
			printf("Input Error\n");
			flag=2;
			break;
	}

 	if(flag==1)
		break;
	if(flag==2)
	{
		flag=0;
		continue;
	}
	
	if(ioctl(fd,cmd,&arg)<0)
	{
		printf("Call ioctl error!\n");
		return -1;
	}
    } 
    close(fd);
    return 0;    
}
           

Makefile

ifneq ($(KERNELRELEASE),)

obj-m := testled.o

else

KDIR := /home/liye/forlinux/linux-2.6.36

all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
install:
	cp testled.ko test /home/liye/forlinux/rootfs/course
clean:
	rm -f *.o *.ko *.mod.c *.mod.o *.order *.symvers

endif