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









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章