smart210 key驱动

key_drive.c

#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
int key_major=0;
int key_minor=0;

struct cdev keycdev;
struct class *key_class;
static struct device *key_device;
#define KEYCLASSNAME   "mykeyclass"
const char keydevname[]={"mykey"};


#define GPH2CON   0xE0200C40
#define GPH2DAT   0xE0200C44
int *gph2con;
int *gph2dat;


struct key_struct{
	unsigned int irq;
	unsigned int pin;
	unsigned char value;
	const char *name;
};
//DECLARE_WAIT_QUEUE_HEAD定义并初始化一个等待队列头:
//相当于wait_queue_head_t   my_key_waitq;加  init_waitqueue_head ( &my_key_waitq )
DECLARE_WAIT_QUEUE_HEAD(my_key_waitq);
static unsigned char pressed = 0;
static unsigned char key_value = 0;
struct fasync_struct * fasync_queue;


struct key_struct key_desc[]={
	{IRQ_EINT(16),S5PV210_GPH2(0),0x01,"key1"},
	{IRQ_EINT(17),S5PV210_GPH2(1),0x02,"key2"},
	{IRQ_EINT(18),S5PV210_GPH2(2),0x04,"key3"},
	{IRQ_EINT(19),S5PV210_GPH2(3),0x08,"key4"},

};

void keyioremap(void)
{
	gph2con=ioremap(GPH2CON, 4); //IO映射:将物理地址GPH2CON转为虚拟地址gph2con,转换长度4个字节
	gph2dat=ioremap(GPH2DAT, 4);	
}
void keyunremap(void)
{
	iounmap(gph2con);  //解除虚拟地址为gph2con的IO映射 与 ioremap相反
	iounmap(gph2dat);		
}

 

int mykeyfasync (int fd, struct file *fops, int onoff)
{
    /*
	注册fasync
	struct fasync_struct {
	spinlock_t		fa_lock;
	int			magic;
	int			fa_fd;
	struct fasync_struct	*fa_next; //singly linked list 
	struct file		*fa_file;
	struct rcu_head		fa_rcu;
    };
    fasync_helper初始化一个fasync_struct结构体,该结构体描述了将要发送信号的进程 PID (fasync_struct->fa_file->f_owner->pid)  
	*/
    printk(KERN_INFO "mykeyfasync...\n");
	return 	fasync_helper( fd, fops, onoff, &fasync_queue);
}
irqreturn_t handler(int irq, void *dev_id)
{

	volatile struct key_struct key_dr = *(volatile struct key_struct*)dev_id;//通dev_id传参
	int value;
	
	value=gpio_get_value(key_dr.pin);  //得到该pin电平
	if(value==0)
	{
		key_value = key_dr.value ;
		pressed = 1;
	}else
		pressed=0;
	printk(KERN_INFO "irq:%d,gpio_get_value:%d;  key_dr.value:%d   key_dr.name:%s\n",irq,value,key_dr.value,key_dr.name);
	wake_up_interruptible(&my_key_waitq);//pressed = 1;且调用wake_up_interruptibl才能唤醒等待队列
	/*
    发送信号SIGIO信号给fasync_struct 结构体所描述的PID,触发应用程序的SIGIO信号处理函数
	*/
	kill_fasync(&fasync_queue, SIGIO, POLLIN | POLLRDNORM);
	return 0;
}
/*
POLLIN
有数据可读。
POLLRDNORM
有普通数据可读。
POLLRDBAND
有优先数据可读。
POLLPRI
有紧迫数据可读。
POLLOUT
写数据不会导致阻塞。
POLLWRNORM
写普通数据不会导致阻塞。
POLLWRBAND
写优先数据不会导致阻塞。
POLLMSG


POLLER
指定的文件描述符发生错误。
POLLHUP
指定的文件描述符挂起事件。
POLLNVAL
指定的文件描述符非法。
*/


void keyinit(void)
{
	keyioremap();	
	writel(0xffffffff,gph2con); //写32bit到gph2con
}


void  keyrequestirq(void)
{
	int i=0,ret;
	for(i=0;i<(sizeof(key_desc)/sizeof(struct key_struct));i++)
	{
		printk(KERN_INFO "irq:%d  name: %s \n",key_desc[i].irq, key_desc[i].name);
		/*
		 //注册中断,中断共享一个处理函数
		* @handler:    interrupt handler function  当发送中断时的回调函数
		* @flags:  flags (see IRQF_* above)  这里为下降沿触发中断 //Z:\kernel\linux-3.0.8\include\linux\irq.h
		* @name:   name of the device  设备名
		* @dev_id: cookie to identify the device 一般传入该设备自定义得结构体,通过该结构体可以区分是哪个中断产生的,也可以通过中断号来区分
		* @irq:    interrupt number  中断号
		*/
		ret=request_irq(key_desc[i].irq, handler,IRQ_TYPE_EDGE_FALLING, key_desc[i].name, &key_desc[i]);
		if(ret) {
			printk(KERN_INFO "ret is %d\n", ret);
			break;
		}
	}

 
}
void keyfreeirq(void)
{
	int i=0;
	for(i=0;i<(sizeof(key_desc)/sizeof(struct key_struct));i++)
	{
		free_irq(key_desc[i].irq, &key_desc[i]); //注销中断
	}
}
int keyopen (struct inode *inode, struct file *fops)
{
	keyrequestirq();
	return 0;
}
int keyrelease (struct inode *inode, struct file *fops)
{
	keyfreeirq();
	return 0;
}
ssize_t keyread (struct file *fops, char __user *buf, size_t n, loff_t *offer)
{
	int ret;
    if(n != 1) {   //一个参数
        printk(KERN_EMERG"The driver can only give one key value once!\n");
        return -EINVAL;
    }

    if(fops->f_flags & O_NONBLOCK) {  
        if(!pressed)
            return -EBUSY;
    } else {
        wait_event_interruptible(my_key_waitq, pressed);/*pressed为1时继续执行后面,否则休眠,休眠时可以中断*/
        pressed = 0;

        ret = copy_to_user(buf, &key_value, 1);  //将内核空间数据复制到用户空间
		printk(KERN_INFO "key: %d\n",key_value);
        if(ret) {
            printk(KERN_EMERG"key copy_to_user error\n");
            return -ENOMEM;
        }
    }
    return 0;

}

static unsigned int keypoll(struct file *filep, poll_table *wait)
{
    unsigned int mask = 0;
    poll_wait(filep, &my_key_waitq, wait);将my_key_waitq等待队列添加到poll_table *wait表中管理
    if(pressed) {
        mask |= POLLIN | POLLRDNORM;  //有值可读
    }
    printk(KERN_INFO "keypoll..\n");
    return mask;
}


const struct file_operations keyfops={
	.owner    =   THIS_MODULE,	
	.open=keyopen,
	.release=keyrelease,
	.read=keyread,
	.fasync=mykeyfasync,  //异步通知,即驱动设备通知应用程序
    .poll=keypoll,

};

static int __init keydriveinit(void)
{
	int ret;

	dev_t devnum=MKDEV(key_major,key_minor);
	cdev_init(&keycdev,&keyfops); //初始化字符设备
	if(key_major)
	{
	ret=register_chrdev_region(devnum, 1, keydevname);  //静态注册设备号
	}else
	{
	ret=alloc_chrdev_region(&devnum, 0, 1, keydevname);//动态申请设备号,由内核分配

	}
	if(ret)
	{
		printk(KERN_INFO "register char dev failure...\n");
		goto myregister_error;
	}
	key_major=MAJOR(devnum);
	key_minor=MINOR(devnum);
	ret=cdev_add(&keycdev, devnum, 1);  //将字符设备添加到内核
	if(ret)
	{
		printk(KERN_INFO "cdev_add  failure...\n");
		goto mycdev_add_error;
	}

	 key_class= class_create(THIS_MODULE, KEYCLASSNAME);//创建一个class  将在/sys/class/下产生一个名为KEYCLASSNAME的类
    if(IS_ERR(key_class)) {   //核查该类是否创建成功
        printk(KERN_INFO "class_create failure %ld\n",PTR_ERR(key_class));	
        goto myclass_error;
    }
    
    key_device= device_create(key_class, NULL, devnum, NULL, KEYCLASSNAME); //创建设备文件节点
    if(IS_ERR(key_device)) {
        printk(KERN_INFO "device_create failure %ld\n",PTR_ERR(key_device));	
        goto mydevice_error;
    }
	keyinit();  //初始化硬件
	printk("key drive init ok. major:%d,minor:%d,drive name:%s\n",key_major,key_minor,keydevname);
	return 0;

mydevice_error:
	device_destroy(key_class,devnum);//销毁字符设备节点
myclass_error:
	class_destroy(key_class);//销毁类
mycdev_add_error:
	cdev_del(&keycdev);//从内核中移除该字符设备
myregister_error:
	unregister_chrdev_region(devnum, 1);//注销设备号
	
	return ret;	
}
static void __exit keydriveexit(void)
{
	keyunremap();
	
	device_destroy(key_class,MKDEV(key_major, key_minor));

	class_destroy(key_class);

	cdev_del(&keycdev);

	unregister_chrdev_region(MKDEV(key_major, key_minor), 1);

	printk(KERN_INFO "keydriveexit...\n");
}
module_init(keydriveinit);
module_exit(keydriveexit);
MODULE_AUTHOR("jump");
MODULE_LICENSE("Dual BSD/GPL");

key_user.c 测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>

#include <asm-generic/errno-base.h>
#if 1   //异步通知fasync中断测试
int fd;
void signfunc(void) 
{
	unsigned char value;
	int rec=read(fd,(void *)&value,1); // 在通知函数读取哪个按键按下
	printf("signfunc...rec:%d   value:%d \n",rec,value);
}
int main()
{
    int oflags;

    fd = open("/dev/mykeyclass", O_RDWR);
    if(fd < 0) {
        printf("/dev/mykeyclass open error\n");
        return -EBUSY;
    }

    signal(SIGIO, (void *)&signfunc);   //设置驱动异步通知的处理函数为signfunc,又驱动调用kill_fasync来通知应用程序
    fcntl(fd, F_SETOWN, getpid());//设置将要在文件描述符fd上接收信号SIGIO的进程为本应用程序id,即异步通知的对象为本应用程序
    oflags = fcntl(fd, F_GETFL);//读取文件状态标志
    fcntl(fd, F_SETFL, oflags | FASYNC);//设置文件状态标志


    while(1) {
        //sleep(2);
    }
	return 0;
}

#else

//中断poll测试程序:
int main()
{
    int fd;
    int ret;
    unsigned char key_value;
    struct pollfd fds[1];

    
    fd = open("/dev/mykeyclass", O_RDWR);
    if(fd < 0) {
        perror("/dev/mykeyclass open error\n");
        return 0;
    }

    fds[0].fd = fd;
    fds[0].events = POLLIN;
    
    while(1) {
		/*
	   struct pollfd {	   
	   int fd;		   //文件描述符 
	   short events;		 // 等待的事件 
	   short revents;		// 实际发生了的事件 
	   } ; 
	   int poll(struct pollfd fd[], nfds_t nfds, int timeout);
       第二个参数nfds:要监视的描述符的数目
       第三个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果  它的值为-1,poll就永远都不会超时
	   */
        ret = poll(fds, 1, 3000);
		printf("ret = %d\n", ret);
        if(ret == 0) {
            printf("timeout...\n");
        }else {

            read(fd, &key_value, 1);
            printf("key_value=0x%x\n", key_value);
        }
    }
	return 0;
}


#endif









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值