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

混杂设备驱动的ioremap见ok6410学习笔记(9.混杂设备驱动及硬件访问)

简单字符驱动的ioremap驱动

memdev.c

/**************************************************************************
 文件名:            memdev.c
 日期:              2013/06/07  
 头文件:            memdev.h
 功能:              ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分
 环境:              Redhat企业版5  内核版本2.6.18-53.el5 
 作者:              Hao 
 流程:              1.分配设备号(a.静态申请  b.动态分配)
 		     2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名”  b.自动创建)
		     3.设备注册(a.设备注册分配  b.设备注册初始化  c.设备注册添加)
		     4.实现file_operation中的函数
		       ioctl驱动  
		       		  a.初始化cmd
		       		  b.无参数的命令
		                  c.读方向的命令
		                  d.写方向的命令
		                  e.在ioctl函数中命令的实现
		     5.设备注销(cdev_del)
		     6.设备号注销
***************************************************************************/
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include "memdev.h"

static int mem_major = MEMDEV_MAJOR;
module_param(mem_major, int, S_IRUGO);
struct cdev c_dev; 

struct class *mem_class;  /*设备的类*/
int kernel_num=1991;//用一个全局变量吧  要不不能把先写入的数据保存下来
char kernel_buf[40]="hello kernel!!!";

volatile unsigned long GPIOM_VA_BASE;//定义一个全局变量  保存ioremap映射的地址
#define GPIOM_CON_VA 	GPIOM_VA_BASE		
#define GPIOM_DAT_VA 	(GPIOM_VA_BASE+0x4)		
#define GPIOM_PUD_VA 	(GPIOM_VA_BASE+0x8)	

MODULE_AUTHOR("Hao");
MODULE_LICENSE("GPL");


/**************************************************************************
函数名:                    ok6410_led_setup
函数功能:                   ioremap映射 gpio初始化
函数参数:                   无
函数返回值:                	无
***************************************************************************/
static void ok6410_led_setup(void)
{
		unsigned long temp; 
		request_mem_region(0x7f008820,0x10,"led_name");//申请i/o内存
		GPIOM_VA_BASE = (volatile unsigned long)ioremap(0x7f008820, 0x10);//

		
		/*temp&=~0xffff;
		temp|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
		(*(volatile unsigned long *)GPIOM_VA_BASE)=temp;*/
			
		(*(volatile unsigned long *)GPIOM_VA_BASE)&=~0xffff;	
		(*(volatile unsigned long *)GPIOM_VA_BASE)|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
		
		temp=0;
		(*(volatile unsigned long *)GPIOM_DAT_VA)=temp;
		//GPIOM_DAT_VA|=0xf; 
		//GPIOM_DAT_VA=0x00;
		
		
		/*******************或者使用这种方式****************************/
/*		temp&=~0xffff;
		temp|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12); 
		writel(temp, GPIOM_CON_VA);
		
		temp|=0xf;
		temp=0;
		writel(temp, GPIOM_DAT_VA);*/
		
}

/**************************************************************************
函数名:                    ok6410_led_release
函数功能:                   iounmap撤销映射 release_mem_region撤销申请
函数参数:                   无
函数返回值:                	无
***************************************************************************/
static void ok6410_led_release(void)
{
	iounmap((void*)GPIOM_VA_BASE);
	release_mem_region(0x7f008820,0x10);
}
/**************************************************************************
函数名:                     memdev_ioctl
函数功能:                   ioctl实现函数  命令实习函数
函数参数:                   无
函数返回值:                 返回ret为正常执行   返回-EINVAL命令号不正确
***************************************************************************/
static long memdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret=0;
	int err=0;
	int kernel_num=1991;
	//char kernel_buf[20]="hello kernel!!!";
	
	/*先判断命令号是否正确*/
	if (_IOC_TYPE(cmd) != CMD_KTYPE) //获得命令的type类型是否正确
        	return -EINVAL;
    	if (_IOC_NR(cmd) > LED_KCMD)    //获得命令的num类型  是否小于命令个数
        	return -EINVAL;
        	
        	
        	
        /*获命令的数据传输方向   根据各自的方向判断*/	
        if (_IOC_DIR(cmd) & _IOC_READ)
       		 err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));/*此函数是根据
       		 内核空间写的 是用来判断 arg应用程序传来的用户空间 是否有效的  所以对于用户空间来说是写*/
     	else if (_IOC_DIR(cmd) & _IOC_WRITE)
      		 err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));//对于用户空间来说是读   成功返回1  失败返回0
    	if (err) 
        	return -EFAULT;
        	
        
        /*实现CMD的用法*/
        switch(cmd)
        {
        	case LEDR_KCMD:  
        		ret=__put_user(kernel_num, (int *)arg);  //把内核中的int型数据读入用户空间   unsigned long arg就是一个地址值   kernel->arg
        		break;
        	case LEDW_KCMD:
        		ret=__get_user(kernel_num, (int *)arg);   //arg->kernel_num   把用户空间的数据传递给kernel_num
        		printk(KERN_EMERG "WRITE_KCMD is in kernel!!!  kernel_num:%d \n",kernel_num);
        		if(1==kernel_num)
        			{
        					writel(0x0, GPIOM_DAT_VA);//将4个led全部点亮
        			}
        		if(0==kernel_num)
        			{
        					writel(0x1f, GPIOM_DAT_VA);//将4个led全部熄灭
        			}
        		break;
        	default:
        		return -EINVAL;
        		break;
       	}	
        
}


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

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

static const struct file_operations mem_fops =  //定义此字符设备的file_operations
{						//这里是对结构体整体赋值的方式
    .owner = THIS_MODULE, //函数名都可以自己定义  都是函数指针
    .open = mem_open,
    .release = mem_release,
    .unlocked_ioctl=memdev_ioctl,
};

static int memdev_init(void)
{
	ok6410_led_setup();
	int result;
	dev_t dev_num=MKDEV(mem_major,0);//将主设备号和次设备号转换成32位的设备号给   静态申请设备号用的
	if(mem_major)
		result=register_chrdev_region(dev_num,2,"newmemdev");//静态申请设备号为dev_num 2个设备  设备名为“newmemdev” 
	else
	{
		result=alloc_chrdev_region(&dev_num,0,2,"newmemdev");//动态分配设备号   设备名可以在/proc/devices文件中找   与/dev路径找文件不同  因为一个设备文件  一个次设备号
		mem_major = MAJOR(dev_num);//获得主设备号
	}
	if (result < 0)
 	{
	 	 return result;   //判断动态分配是否成功  不成功则退出函数
	}
		 		 mem_class = class_create(THIS_MODULE, "mem_class_name");//创建设备类
      	 device_create(mem_class, NULL, MKDEV(mem_major, 0), NULL, "memdev0"); //创建设备文件 文件名为memdev0
	/*设备注册 分配已经在前面完成了 为全局变量*/
      	cdev_init(&c_dev,&mem_fops);//设备注册初始化  将设备号dev_num和file_operation建立联系
      	c_dev.owner = THIS_MODULE;//*****************貌似可以屏蔽吧*********************
      	cdev_add(&c_dev,dev_num,2);//设备注册  添加   设备号为dev_num 设备数为2个
      	return 0;
}

static int memdev_exit(void)
{
	ok6410_led_release();
	cdev_del(&c_dev);//设备注销
	device_destroy(mem_class, MKDEV(mem_major, 0));/*注销设备*/
	class_destroy(mem_class);  /*删除类*/
	unregister_chrdev_region(MKDEV(mem_major, 0), 2);//设备号注销
	return 0;
}

module_init(memdev_init);
module_exit(memdev_exit);







memdev.h

/**************************************************************************
 文件名:            memdev.h
 日期:              2013/06/07   
 头文件:            memdev.h
 功能:              ioctl字符驱动 (通过三个命令进行ioctl的测试) 此为驱动部分头文件
                     定义三个命令
 环境:              Redhat企业版5  内核版本2.6.18-53.el5 
 作者:              Hao 
 流程:              1.分配设备号(a.静态申请  b.动态分配)
 		     2.创建设备文件(a.手动创建mknod(需要设备号)注“设备名和设备文件名”  b.自动创建)
		     3.设备注册(a.设备注册分配  b.设备注册初始化  c.设备注册添加)
		     4.实现file_operation中的函数
		       ioctl驱动  
		       		  a.初始化cmd
		       		  b.无参数的命令
		                  c.读方向的命令
		                  d.写方向的命令
		     5.设备注销(cdev_del)
		     6.设备号注销
***************************************************************************/
#ifndef _MEMDEV_H_
#define _MEMDEV_H_

#include <linux/ioctl.h>

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 251   /*预设的mem的主设备号*/
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2    /*设备数*/
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096   /*开辟的内存大小*/
#endif

/*mem设备描述结构体*/
struct mem_dev                                     
{                                                        
  char *data;                      
  unsigned long size;       
};

#define CMD_KTYPE 'k' //定义命令幻数   也叫命令类型


#define LEDR_KCMD   _IOR(CMD_KTYPE,1,int)   //定义读方向的命令
#define LEDW_KCMD  _IOW(CMD_KTYPE,2,int)  //定义写方向的命令

#define LED_KCMD 2  //命令个数  后面判断命令是否有效 用的


#endif /* _MEMDEV_H_ */

app_led.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include "memdev.h"

int main(int argc, char *argv[])
{
	int fd=0;
	printf("\n%d\n",*argv[1]);
	unsigned int arg=(unsigned int)(*argv[1]-'0');
	char buf[40]="WRITE_STR_KCMD is in kernel";
	if(-1==(fd=open("/dev/memdev0",O_RDWR)))  //设备节点名称为memdev0
	{
		printf("Open Dev Mem0 Error!\n");
		_exit(EXIT_FAILURE);
	}
	printf("begin WRITE_KCMD!!!\n");  //写入一个int型arg
	ioctl(fd,LEDW_KCMD,&arg);
	close(fd);
	return 0;
}


注意的还是ioremap的返回值的利用。

对应飞凌驱动的测试程序

/*此为ok6410  飞凌提供的驱动的测试程序 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main(int argc,char *argv[])
{
	int fd=0;
	printf("\n%d\n",*argv[1]);
	unsigned int arg=(unsigned int)(*argv[1]-'0');  //驱动中规定有两个参数第一个当命令用 为0的时候是关灯 为1的时候是开灯
	printf("%d\n",arg);
	unsigned int arg1=(unsigned int)(*argv[2]-'0'); //第二参数是第几个灯进行开关
	printf("%d\n",arg1);
	char buf[40]="WRITE_STR_KCMD is in kernel";
	if(-1==(fd=open("/dev/leds",O_RDWR)))  //设备节点名称为memdev0
	{
		printf("Open Dev Mem0 Error!\n");
		exit(EXIT_FAILURE);
	}
	printf("begin WRITE_KCMD!!!\n");  //写入一个int型arg
	ioctl(fd,arg,arg1);
	close(fd);
	return 0;
}


驱动在/drivers/char/s3c6410_leds.c
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
//#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>

#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-m.h>

#define DEVICE_NAME "leds"

static long s3c6410_leds_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	switch(cmd) {
		unsigned tmp;
	case 0:
	case 1:
                 if (arg > 4) 
                 {
		 	return -EINVAL;
		 }
		tmp = readl(S3C64XX_GPMDAT);
            
		if(cmd==0) //close light
                  { 
			tmp &= (~(1<<arg));
                  }
		else  //open light
                  { 
			tmp |= (1<<arg);
                  }

                writel(tmp,S3C64XX_GPMDAT);

		printk (DEVICE_NAME": %d %d\n", arg, cmd);
		return 0;
	default:
		return -EINVAL;
	}
}

static struct file_operations dev_fops = {
	.owner			= THIS_MODULE,
	.unlocked_ioctl	= s3c6410_leds_ioctl,
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret;
        
       unsigned tmp;

       //gpm0-3 pull up
	tmp = readl(S3C64XX_GPMPUD);
	tmp &= (~0xFF);
	tmp |= 0xaa;
	writel(tmp,S3C64XX_GPMPUD);

	//gpm0-3 output mode
	tmp =readl(S3C64XX_GPMCON);
	tmp &= (~0xFFFF);
	tmp |= 0x1111;
	writel(tmp,S3C64XX_GPMCON);
	
	//gpm0-3 output 0
	tmp = __raw_readl(S3C64XX_GPMDAT);
	tmp |= 0x10;
	writel(tmp,S3C64XX_GPMDAT);  

	ret = misc_register(&misc);

	printk (DEVICE_NAME"\tinitialized\n");

	return ret;
}

static void __exit dev_exit(void)
{
	misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FORLINX Inc.");

这个驱动是不能改的,因为是编译进内核的,所有只能用测试程序去适应驱动。2440也有对应的驱动,在国嵌和友善的内核里面。

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